void write_status_text (int no, const char *text) { if (!statusfp || !status_currently_allowed (no) ) return; /* Not enabled or allowed. */ es_fputs ("[GNUPG:] ", statusfp); es_fputs (get_status_string (no), statusfp); if ( text ) { es_putc ( ' ', statusfp); for (; *text; text++) { if (*text == '\n') es_fputs ("\\n", statusfp); else if (*text == '\r') es_fputs ("\\r", statusfp); else es_fputc ( *(const byte *)text, statusfp); } } es_putc ('\n', statusfp); if (es_fflush (statusfp) && opt.exit_on_status_write_error) g10_exit (0); }
/* Same as above but outputs the error code only. */ void write_status_errcode (const char *where, int errcode) { if (!statusfp || !status_currently_allowed (STATUS_ERROR)) return; /* Not enabled or allowed. */ es_fprintf (statusfp, "[GNUPG:] %s %s %u\n", get_status_string (STATUS_ERROR), where, gpg_err_code (errcode)); if (es_fflush (statusfp) && opt.exit_on_status_write_error) g10_exit (0); }
/* This thread feeds data to the given stream. */ static THREAD_RET_TYPE producer_thread (void *argaddr) { struct thread_arg *arg = argaddr; int i = 0; (void)arg; while (!arg->stop_me && i++ < 3) { show ("thread '%s' about to write\n", arg->name); es_fprintf (arg->stream, "This is '%s' count=%d\n", arg->name, i); es_fflush (arg->stream); } es_fclose (arg->stream); return THREAD_RET_VALUE; }
/* Request a string from the client over the command-fd. If GETBOOL is set the function returns a static string (do not free) if the netered value was true or NULL if the entered value was false. */ static char * do_get_from_fd ( const char *keyword, int hidden, int getbool ) { int i, len; char *string; if (statusfp != es_stdout) es_fflush (es_stdout); write_status_text (getbool? STATUS_GET_BOOL : hidden? STATUS_GET_HIDDEN : STATUS_GET_LINE, keyword); for (string = NULL, i = len = 200; ; i++ ) { if (i >= len-1 ) { char *save = string; len += 100; string = hidden? xmalloc_secure ( len ) : xmalloc ( len ); if (save) memcpy (string, save, i ); else i = 0; } /* Fixme: why not use our read_line function here? */ if ( myread( opt.command_fd, string+i, 1) != 1 || string[i] == '\n' ) break; else if ( string[i] == CONTROL_D ) { /* Found ETX - Cancel the line and return a sole ETX. */ string[0] = CONTROL_D; i = 1; break; } } string[i] = 0; write_status (STATUS_GOT_IT); if (getbool) /* Fixme: is this correct??? */ return (string[0] == 'Y' || string[0] == 'y') ? "" : NULL; return string; }
/* Fork and exec the PGMNAME, see exechelp.h for details. */ gpg_error_t gnupg_spawn_process (const char *pgmname, const char *argv[], gpg_err_source_t errsource, void (*preexec)(void), unsigned int flags, estream_t infp, estream_t *r_outfp, estream_t *r_errfp, pid_t *pid) { gpg_error_t err; SECURITY_ATTRIBUTES sec_attr; PROCESS_INFORMATION pi = { NULL, /* Returns process handle. */ 0, /* Returns primary thread handle. */ 0, /* Returns pid. */ 0 /* Returns tid. */ }; STARTUPINFO si; int cr_flags; char *cmdline; HANDLE inhandle = INVALID_HANDLE_VALUE; HANDLE outpipe[2] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE}; HANDLE errpipe[2] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE}; estream_t outfp = NULL; estream_t errfp = NULL; HANDLE nullhd[3] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE}; int i; es_syshd_t syshd; if (r_outfp) *r_outfp = NULL; if (r_errfp) *r_errfp = NULL; *pid = (pid_t)(-1); /* Always required. */ if (infp) { es_fflush (infp); es_rewind (infp); es_syshd (infp, &syshd); switch (syshd.type) { case ES_SYSHD_FD: inhandle = (HANDLE)_get_osfhandle (syshd.u.fd); break; case ES_SYSHD_SOCK: inhandle = (HANDLE)_get_osfhandle (syshd.u.sock); break; case ES_SYSHD_HANDLE: inhandle = syshd.u.handle; break; default: inhandle = INVALID_HANDLE_VALUE; break; } if (inhandle == INVALID_HANDLE_VALUE) return gpg_err_make (errsource, GPG_ERR_INV_VALUE); /* FIXME: In case we can't get a system handle (e.g. due to es_fopencookie we should create a piper and a feeder thread. */ } if (r_outfp) { if (create_inheritable_pipe (outpipe, 1)) { err = gpg_err_make (errsource, GPG_ERR_GENERAL); log_error (_("error creating a pipe: %s\n"), gpg_strerror (err)); return err; } syshd.type = ES_SYSHD_HANDLE; syshd.u.handle = outpipe[0]; outfp = es_sysopen (&syshd, "r"); if (!outfp) { err = gpg_err_make (errsource, gpg_err_code_from_syserror ()); log_error (_("error creating a stream for a pipe: %s\n"), gpg_strerror (err)); CloseHandle (outpipe[0]); CloseHandle (outpipe[1]); outpipe[0] = outpipe[1] = INVALID_HANDLE_VALUE; return err; } } if (r_errfp) { if (create_inheritable_pipe (errpipe, 1)) { err = gpg_err_make (errsource, GPG_ERR_GENERAL); log_error (_("error creating a pipe: %s\n"), gpg_strerror (err)); return err; } syshd.type = ES_SYSHD_HANDLE; syshd.u.handle = errpipe[0]; errfp = es_sysopen (&syshd, "r"); if (!errfp) { err = gpg_err_make (errsource, gpg_err_code_from_syserror ()); log_error (_("error creating a stream for a pipe: %s\n"), gpg_strerror (err)); CloseHandle (errpipe[0]); CloseHandle (errpipe[1]); errpipe[0] = errpipe[1] = INVALID_HANDLE_VALUE; if (outfp) es_fclose (outfp); else if (outpipe[0] != INVALID_HANDLE_VALUE) CloseHandle (outpipe[0]); if (outpipe[1] != INVALID_HANDLE_VALUE) CloseHandle (outpipe[1]); return err; } } /* Prepare security attributes. */ memset (&sec_attr, 0, sizeof sec_attr ); sec_attr.nLength = sizeof sec_attr; sec_attr.bInheritHandle = FALSE; /* Build the command line. */ err = build_w32_commandline (pgmname, argv, &cmdline); if (err) return err; if (inhandle != INVALID_HANDLE_VALUE) nullhd[0] = w32_open_null (0); if (outpipe[1] != INVALID_HANDLE_VALUE) nullhd[1] = w32_open_null (0); if (errpipe[1] != INVALID_HANDLE_VALUE) nullhd[2] = w32_open_null (0); /* Start the process. Note that we can't run the PREEXEC function because this might change our own environment. */ (void)preexec; memset (&si, 0, sizeof si); si.cb = sizeof (si); si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; si.wShowWindow = DEBUG_W32_SPAWN? SW_SHOW : SW_MINIMIZE; si.hStdInput = inhandle == INVALID_HANDLE_VALUE? nullhd[0] : inhandle; si.hStdOutput = outpipe[1] == INVALID_HANDLE_VALUE? nullhd[1] : outpipe[1]; si.hStdError = errpipe[1] == INVALID_HANDLE_VALUE? nullhd[2] : errpipe[1]; cr_flags = (CREATE_DEFAULT_ERROR_MODE | ((flags & 128)? DETACHED_PROCESS : 0) | GetPriorityClass (GetCurrentProcess ()) | CREATE_SUSPENDED); /* log_debug ("CreateProcess, path=`%s' cmdline=`%s'\n", pgmname, cmdline); */ if (!CreateProcess (pgmname, /* Program to start. */ cmdline, /* Command line arguments. */ &sec_attr, /* Process security attributes. */ &sec_attr, /* Thread security attributes. */ TRUE, /* Inherit handles. */ cr_flags, /* Creation flags. */ NULL, /* Environment. */ NULL, /* Use current drive/directory. */ &si, /* Startup information. */ &pi /* Returns process information. */ )) { log_error ("CreateProcess failed: %s\n", w32_strerror (-1)); xfree (cmdline); if (outfp) es_fclose (outfp); else if (outpipe[0] != INVALID_HANDLE_VALUE) CloseHandle (outpipe[0]); if (outpipe[1] != INVALID_HANDLE_VALUE) CloseHandle (outpipe[1]); if (errfp) es_fclose (errfp); else if (errpipe[0] != INVALID_HANDLE_VALUE) CloseHandle (errpipe[0]); if (errpipe[1] != INVALID_HANDLE_VALUE) CloseHandle (errpipe[1]); return gpg_err_make (errsource, GPG_ERR_GENERAL); } xfree (cmdline); cmdline = NULL; /* Close the inherited handles to /dev/null. */ for (i=0; i < DIM (nullhd); i++) if (nullhd[i] != INVALID_HANDLE_VALUE) CloseHandle (nullhd[i]); /* Close the inherited ends of the pipes. */ if (outpipe[1] != INVALID_HANDLE_VALUE) CloseHandle (outpipe[1]); if (errpipe[1] != INVALID_HANDLE_VALUE) CloseHandle (errpipe[1]); /* log_debug ("CreateProcess ready: hProcess=%p hThread=%p" */ /* " dwProcessID=%d dwThreadId=%d\n", */ /* pi.hProcess, pi.hThread, */ /* (int) pi.dwProcessId, (int) pi.dwThreadId); */ /* log_debug (" outfp=%p errfp=%p\n", outfp, errfp); */ /* Fixme: For unknown reasons AllowSetForegroundWindow returns an invalid argument error if we pass it the correct processID. As a workaround we use -1 (ASFW_ANY). */ if ( (flags & 64) ) gnupg_allow_set_foregound_window ((pid_t)(-1)/*pi.dwProcessId*/); /* Process has been created suspended; resume it now. */ ResumeThread (pi.hThread); CloseHandle (pi.hThread); if (r_outfp) *r_outfp = outfp; if (r_errfp) *r_errfp = errfp; *pid = handle_to_pid (pi.hProcess); return 0; }
/* Fork and exec the PGMNAME, see exechelp.h for details. */ gpg_error_t gnupg_spawn_process (const char *pgmname, const char *argv[], gpg_err_source_t errsource, void (*preexec)(void), unsigned int flags, estream_t infp, estream_t *r_outfp, estream_t *r_errfp, pid_t *pid) { gpg_error_t err; int infd = -1; int outpipe[2] = {-1, -1}; int errpipe[2] = {-1, -1}; estream_t outfp = NULL; estream_t errfp = NULL; (void)flags; /* Currently not used. */ if (r_outfp) *r_outfp = NULL; if (r_errfp) *r_errfp = NULL; *pid = (pid_t)(-1); /* Always required. */ if (infp) { es_fflush (infp); es_rewind (infp); infd = es_fileno (infp); if (infd == -1) return gpg_err_make (errsource, GPG_ERR_INV_VALUE); } if (r_outfp) { err = create_pipe_and_estream (outpipe, &outfp, errsource); if (err) return err; } if (r_errfp) { err = create_pipe_and_estream (errpipe, &errfp, errsource); if (err) { if (outfp) es_fclose (outfp); else if (outpipe[0] != -1) close (outpipe[0]); if (outpipe[1] != -1) close (outpipe[1]); return err; } } *pid = fork (); if (*pid == (pid_t)(-1)) { err = gpg_err_make (errsource, gpg_err_code_from_syserror ()); log_error (_("error forking process: %s\n"), gpg_strerror (err)); if (outfp) es_fclose (outfp); else if (outpipe[0] != -1) close (outpipe[0]); if (outpipe[1] != -1) close (outpipe[1]); if (errfp) es_fclose (errfp); else if (errpipe[0] != -1) close (errpipe[0]); if (errpipe[1] != -1) close (errpipe[1]); return err; } if (!*pid) { /* This is the child. */ gcry_control (GCRYCTL_TERM_SECMEM); es_fclose (outfp); es_fclose (errfp); do_exec (pgmname, argv, infd, outpipe[1], errpipe[1], preexec); /*NOTREACHED*/ } /* This is the parent. */ if (outpipe[1] != -1) close (outpipe[1]); if (errpipe[1] != -1) close (errpipe[1]); if (r_outfp) *r_outfp = outfp; if (r_errfp) *r_errfp = errfp; return 0; }
/* * Write a status line with a buffer using %XX escapes. If WRAP is > * 0 wrap the line after this length. If STRING is not NULL it will * be prepended to the buffer, no escaping is done for string. * A wrap of -1 forces spaces not to be encoded as %20. */ void write_status_text_and_buffer (int no, const char *string, const char *buffer, size_t len, int wrap) { const char *s, *text; int esc, first; int lower_limit = ' '; size_t n, count, dowrap; if (!statusfp || !status_currently_allowed (no)) return; /* Not enabled or allowed. */ if (wrap == -1) { lower_limit--; wrap = 0; } text = get_status_string (no); count = dowrap = first = 1; do { if (dowrap) { es_fprintf (statusfp, "[GNUPG:] %s ", text); count = dowrap = 0; if (first && string) { es_fputs (string, statusfp); count += strlen (string); /* Make sure that there is a space after the string. */ if (*string && string[strlen (string)-1] != ' ') { es_putc (' ', statusfp); count++; } } first = 0; } for (esc=0, s=buffer, n=len; n && !esc; s++, n--) { if (*s == '%' || *(const byte*)s <= lower_limit || *(const byte*)s == 127 ) esc = 1; if (wrap && ++count > wrap) { dowrap=1; break; } } if (esc) { s--; n++; } if (s != buffer) es_fwrite (buffer, s-buffer, 1, statusfp); if ( esc ) { es_fprintf (statusfp, "%%%02X", *(const byte*)s ); s++; n--; } buffer = s; len = n; if (dowrap && len) es_putc ('\n', statusfp); } while (len); es_putc ('\n',statusfp); if (es_fflush (statusfp) && opt.exit_on_status_write_error) g10_exit (0); }
/* Fork and exec the PGMNAME, see exechelp.h for details. */ gpg_error_t gnupg_spawn_process (const char *pgmname, const char *argv[], gpg_err_source_t errsource, void (*preexec)(void), unsigned int flags, estream_t infp, estream_t *r_outfp, estream_t *r_errfp, pid_t *pid) { gpg_error_t err; PROCESS_INFORMATION pi = {NULL }; char *cmdline; es_syshd_t syshd; struct { HANDLE hd; int rvid; } inpipe = {INVALID_HANDLE_VALUE, 0}; struct { HANDLE hd; int rvid; } outpipe = {INVALID_HANDLE_VALUE, 0}; struct { HANDLE hd; int rvid; } errpipe = {INVALID_HANDLE_VALUE, 0}; estream_t outfp = NULL; estream_t errfp = NULL; (void)preexec; (void)flags; /* Setup return values. */ if (r_outfp) *r_outfp = NULL; if (r_errfp) *r_errfp = NULL; *pid = (pid_t)(-1); /* Always required. */ log_debug ("%s: enter\n", __func__); if (infp) { es_fflush (infp); es_rewind (infp); /* Create a pipe to copy our infile to the stdin of the child process. On success inpipe.hd is owned by the feeder. */ inpipe.hd = _assuan_w32ce_prepare_pipe (&inpipe.rvid, 1); if (inpipe.hd == INVALID_HANDLE_VALUE) { log_error ("_assuan_w32ce_prepare_pipe failed: %s\n", w32_strerror (-1)); gpg_err_set_errno (EIO); return gpg_error_from_syserror (); } log_debug ("%s: inpipe %p created; hd=%p rvid=%d\n", __func__, infp, inpipe.hd, inpipe.rvid); err = start_feeder (infp, inpipe.hd, 1); if (err) { log_error ("error spawning feeder: %s\n", gpg_strerror (err)); CloseHandle (inpipe.hd); return err; } inpipe.hd = INVALID_HANDLE_VALUE; /* Now owned by the feeder. */ log_debug ("%s: inpipe %p created; feeder started\n", __func__, infp); } if (r_outfp) { /* Create a pipe to make the stdout of the child process available as a stream. */ outpipe.hd = _assuan_w32ce_prepare_pipe (&outpipe.rvid, 0); if (outpipe.hd == INVALID_HANDLE_VALUE) { log_error ("_assuan_w32ce_prepare_pipe failed: %s\n", w32_strerror (-1)); gpg_err_set_errno (EIO); /* Fixme release other stuff/kill feeder. */ return gpg_error_from_syserror (); } syshd.type = ES_SYSHD_HANDLE; syshd.u.handle = outpipe.hd; err = 0; outfp = es_sysopen (&syshd, "r"); if (!outfp) { err = gpg_err_make (errsource, gpg_err_code_from_syserror ()); log_error ("error opening pipe stream: %s\n", gpg_strerror (err)); CloseHandle (outpipe.hd); return err; } log_debug ("%s: outpipe %p created; hd=%p rvid=%d\n", __func__, outfp, outpipe.hd, outpipe.rvid); outpipe.hd = INVALID_HANDLE_VALUE; /* Now owned by the OUTFP. */ } if (r_errfp) { /* Create a pipe to make the stderr of the child process available as a stream. */ errpipe.hd = _assuan_w32ce_prepare_pipe (&errpipe.rvid, 0); if (errpipe.hd == INVALID_HANDLE_VALUE) { log_error ("_assuan_w32ce_prepare_pipe failed: %s\n", w32_strerror (-1)); gpg_err_set_errno (EIO); /* Fixme release other stuff/kill feeder. */ return gpg_error_from_syserror (); } syshd.type = ES_SYSHD_HANDLE; syshd.u.handle = errpipe.hd; err = 0; errfp = es_sysopen (&syshd, "r"); if (!errfp) { err = gpg_err_make (errsource, gpg_err_code_from_syserror ()); log_error ("error opening pipe stream: %s\n", gpg_strerror (err)); CloseHandle (errpipe.hd); return err; } log_debug ("%s: errpipe %p created; hd=%p rvid=%d\n", __func__, errfp, errpipe.hd, errpipe.rvid); errpipe.hd = INVALID_HANDLE_VALUE; /* Now owned by the ERRFP. */ } /* Build the command line. */ err = build_w32_commandline (argv, inpipe.rvid, outpipe.rvid, errpipe.rvid, &cmdline); if (err) { /* Fixme release other stuff/kill feeder. */ CloseHandle (errpipe.hd); return err; } log_debug ("CreateProcess, path='%s' cmdline='%s'\n", pgmname, cmdline); if (!create_process (pgmname, cmdline, &pi)) { log_error ("CreateProcess failed: %s\n", w32_strerror (-1)); xfree (cmdline); /* Fixme release other stuff/kill feeder. */ CloseHandle (errpipe.hd); return gpg_error (GPG_ERR_GENERAL); } xfree (cmdline); cmdline = NULL; /* Note: The other end of the pipe is a rendezvous id and thus there is no need for a close. */ log_debug ("CreateProcess ready: hProcess=%p hThread=%p" " dwProcessID=%d dwThreadId=%d\n", pi.hProcess, pi.hThread, (int) pi.dwProcessId, (int) pi.dwThreadId); /* Process has been created suspended; resume it now. */ ResumeThread (pi.hThread); CloseHandle (pi.hThread); if (r_outfp) *r_outfp = outfp; if (r_errfp) *r_errfp = errfp; *pid = handle_to_pid (pi.hProcess); return 0; }
static void test_poll (void) { int ret; gpgrt_poll_t fds[3]; char buffer[16]; size_t used, nwritten; int c; memset (fds, 0, sizeof fds); fds[0].stream = test_stdin; fds[0].want_read = 1; fds[1].stream = test_stdout; fds[1].want_write = 1; /* FIXME: We don't use the next stream at all. */ fds[2].stream = test_stderr; fds[2].want_write = 1; fds[2].ignore = 1; used = 0; while (used || !fds[0].ignore) { ret = gpgrt_poll (fds, DIM(fds), -1); if (ret == -1) { fail ("gpgrt_poll failed: %s\n", strerror (errno)); continue; } if (!ret) { fail ("gpgrt_poll unexpectedly timed out\n"); continue; } show ("gpgrt_poll detected %d events\n", ret); if (fds[0].got_read) { /* Read from the producer. */ for (;;) { c = es_fgetc (fds[0].stream); if (c == EOF) { if (es_feof (fds[0].stream)) { show ("reading '%s': EOF\n", peer_stdin.name); fds[0].ignore = 1; /* Not anymore needed. */ peer_stdin.stop_me = 1; /* Tell the thread to stop. */ } else if (es_ferror (fds[0].stream)) { fail ("error reading '%s': %s\n", peer_stdin.name, strerror (errno)); fds[0].ignore = 1; /* Disable. */ peer_stdin.stop_me = 1; /* Tell the thread to stop. */ } else show ("reading '%s': EAGAIN\n", peer_stdin.name); break; } else { if (used <= sizeof buffer -1) buffer[used++] = c; if (used == sizeof buffer) { show ("throttling reading from '%s'\n", peer_stdin.name); fds[0].ignore = 1; break; } } } show ("read from '%s': %zu bytes\n", peer_stdin.name, used); if (used) fds[1].ignore = 0; /* Data to send. */ } if (fds[1].got_write) { if (used) { ret = es_write (fds[1].stream, buffer, used, &nwritten); show ("result for writing to '%s': ret=%d, n=%zu, nwritten=%zu\n", peer_stdout.name, ret, used, nwritten); if (!ret) { assert (nwritten <= used); /* Move the remaining data to the front of buffer. */ memmove (buffer, buffer + nwritten, sizeof buffer - nwritten); used -= nwritten; } ret = es_fflush (fds[1].stream); if (ret) fail ("Flushing for '%s' failed: %s\n", peer_stdout.name, strerror (errno)); } if (!used) fds[1].ignore = 1; /* No need to send data. */ } if (used < sizeof buffer / 2 && !peer_stdin.stop_me && fds[0].ignore) { show ("accelerate reading from '%s'\n", peer_stdin.name); fds[0].ignore = 0; } } }
/* Show a prompt and allow the user to select keys for retrieval. */ static gpg_error_t show_prompt (ctrl_t ctrl, KEYDB_SEARCH_DESC *desc, int numdesc, int count, const char *search) { gpg_error_t err; char *answer = NULL; es_fflush (es_stdout); if (count && opt.command_fd == -1) { static int from = 1; tty_printf ("Keys %d-%d of %d for \"%s\". ", from, numdesc, count, search); from = numdesc + 1; } again: err = 0; xfree (answer); answer = cpr_get_no_help ("keysearch.prompt", _("Enter number(s), N)ext, or Q)uit > ")); /* control-d */ if (answer[0]=='\x04') { tty_printf ("Q\n"); answer[0] = 'q'; } if (answer[0]=='q' || answer[0]=='Q') err = gpg_error (GPG_ERR_CANCELED); else if (atoi (answer) >= 1 && atoi (answer) <= numdesc) { char *split = answer; char *num; int numarray[50]; int numidx = 0; int idx; while ((num = strsep (&split, " ,"))) if (atoi (num) >= 1 && atoi (num) <= numdesc) { if (numidx >= DIM (numarray)) { tty_printf ("Too many keys selected\n"); goto again; } numarray[numidx++] = atoi (num); } if (!numidx) goto again; { KEYDB_SEARCH_DESC *selarray; selarray = xtrymalloc (numidx * sizeof *selarray); if (!selarray) { err = gpg_error_from_syserror (); goto leave; } for (idx = 0; idx < numidx; idx++) selarray[idx] = desc[numarray[idx]-1]; err = keyserver_get (ctrl, selarray, numidx, NULL); xfree (selarray); } } leave: xfree (answer); return err; }