void cmd_list(struct_client *clt) { int oldfd; if (clt->data.transfert_mode == UNSET) { printf("520 LIST you have to use PORT or PASV before\n"); return ; } if (clt->data.transfert_mode == PASV) list_passiv(clt); else if (clt->data.transfert_mode == PORT) { printf("150 Opening BINARY mode data connection for 'ls'\n"); if (connect_to_client(clt) == -1) { printf("520 LIST impossible to reach client\n"); return ; } oldfd = xdup(1); xdup2(clt->data.sock, 1); system("ls -la"); xdup2(oldfd, 1); printf("226 Transfer complete\n"); close_data_chan(clt, -1); } }
pid_t sys_start_dedup_filter_child(void) { pid_t cpid; if (dedup_filter_command_option == NULL) return -1; xpipe(dedup_filter_query_pipe); xpipe(dedup_filter_response_pipe); cpid = xfork(); if (cpid > 0) { // parent tar // we write to query pipe, read from response pipe // so close the other fds xclose(dedup_filter_query_pipe[PREAD]); xclose(dedup_filter_response_pipe[PWRITE]); return cpid; } // child (filter) reads from query and writes to response // so close the rest xclose(dedup_filter_query_pipe[PWRITE]); xclose(dedup_filter_response_pipe[PREAD]); xdup2(dedup_filter_query_pipe[PREAD], STDIN_FILENO); xdup2(dedup_filter_response_pipe[PWRITE], STDOUT_FILENO); program_name = _("tar (child)"); execlp(dedup_filter_command_option, NULL); }
/* Callback called by glib main loop when a client connects to ABRT's socket. */ static gboolean server_socket_cb(GIOChannel *source, GIOCondition condition, gpointer ptr_unused) { kill_idle_timeout(); load_abrt_conf(); int socket = accept(g_io_channel_unix_get_fd(source), NULL, NULL); if (socket == -1) { perror_msg("accept"); goto server_socket_finitio; } log_notice("New client connected"); fflush(NULL); /* paranoia */ int pipefd[2]; xpipe(pipefd); pid_t pid = fork(); if (pid < 0) { perror_msg("fork"); close(socket); close(pipefd[0]); close(pipefd[1]); goto server_socket_finitio; } if (pid == 0) /* child */ { xdup2(socket, STDIN_FILENO); xdup2(socket, STDOUT_FILENO); close(socket); close(pipefd[0]); xmove_fd(pipefd[1], STDERR_FILENO); char *argv[3]; /* abrt-server [-s] NULL */ char **pp = argv; *pp++ = (char*)"abrt-server"; if (logmode & LOGMODE_JOURNAL) *pp++ = (char*)"-s"; *pp = NULL; execvp(argv[0], argv); perror_msg_and_die("Can't execute '%s'", argv[0]); } /* parent */ close(socket); close(pipefd[1]); add_abrt_server_proc(pid, pipefd[0]); server_socket_finitio: start_idle_timeout(); return TRUE; }
static void start_logging(void) { /* Open stdin to /dev/null */ xmove_fd(xopen("/dev/null", O_RDWR), STDIN_FILENO); /* We must not leave fds 0,1,2 closed. * Otherwise fprintf(stderr) dumps messages into random fds, etc. */ xdup2(STDIN_FILENO, STDOUT_FILENO); xdup2(STDIN_FILENO, STDERR_FILENO); logmode = LOGMODE_JOURNAL; putenv((char*)"ABRT_SYSLOG=1"); }
static int manage_child(t_fifo_elem *cur, t_sllist **myenv) { if (cur->data.fd[IN]) xdup2(cur->data.fd[IN], 0); if (cur->data.fd[OUT]) xdup2(cur->data.fd[OUT], 1); if (cur->data.flag[ERR]) xdup2(cur->data.fd[OUT], 2); execute_extern(cur->data.argv, myenv); return (EXIT_SUCCESS); }
int switch_root_main(int argc UNUSED_PARAM, char **argv) { char *newroot, *console = NULL; struct stat st1, st2; struct statfs stfs; dev_t rootdev; // Parse args (-c console) opt_complementary = "-2"; // minimum 2 params getopt32(argv, "+c:", &console); // '+': stop parsing at first non-option argv += optind; // Change to new root directory and verify it's a different fs. newroot = *argv++; xchdir(newroot); if (lstat(".", &st1) || lstat("/", &st2) || st1.st_dev == st2.st_dev) { bb_error_msg_and_die("bad newroot %s", newroot); } rootdev = st2.st_dev; // Additional sanity checks: we're about to rm -rf /, so be REALLY SURE // we mean it. (I could make this a CONFIG option, but I would get email // from all the people who WILL eat their filesystems.) if (lstat("/init", &st1) || !S_ISREG(st1.st_mode) || statfs("/", &stfs) || (((unsigned)stfs.f_type != RAMFS_MAGIC) && ((unsigned)stfs.f_type != TMPFS_MAGIC)) || (getpid() != 1) ) { bb_error_msg_and_die("not rootfs"); } // Zap everything out of rootdev delete_contents("/", rootdev); // Overmount / with newdir and chroot into it. The chdir is needed to // recalculate "." and ".." links. if (mount(".", "/", NULL, MS_MOVE, NULL)) bb_error_msg_and_die("error moving root"); xchroot("."); xchdir("/"); // If a new console specified, redirect stdin/stdout/stderr to that. if (console) { close(0); xopen(console, O_RDWR); xdup2(0, 1); xdup2(0, 2); } // Exec real init. (This is why we must be pid 1.) execv(argv[0], argv); bb_perror_msg_and_die("bad init %s", argv[0]); }
static void startservice(struct svdir *s) { int p; char *run[2]; if (s->state == S_FINISH) run[0] = (char*)"./finish"; else { run[0] = (char*)"./run"; custom(s, 'u'); } run[1] = NULL; if (s->pid != 0) stopservice(s); /* should never happen */ while ((p = vfork()) == -1) { warn_cannot("vfork, sleeping"); sleep(5); } if (p == 0) { /* child */ if (haslog) { /* NB: bug alert! right order is close, then dup2 */ if (s->islog) { xchdir("./log"); close(logpipe.wr); xdup2(logpipe.rd, 0); } else { close(logpipe.rd); xdup2(logpipe.wr, 1); } } bb_signals(0 + (1 << SIGCHLD) + (1 << SIGTERM) , SIG_DFL); sig_unblock(SIGCHLD); sig_unblock(SIGTERM); execvp(*run, run); fatal2_cannot(s->islog ? "start log/" : "start ", *run); } /* parent */ if (s->state != S_FINISH) { gettimeofday_ns(&s->start); s->state = S_RUN; } s->pid = p; pidchanged = 1; s->ctrl = C_NOOP; update_status(s); }
int main(int argc, char **argv) { extern char **environ; t_sllist *myenv; int fd; build_env(environ, &myenv); if (argc > 1) { if (!access(argv[1], R_OK)) { fd = xopen(argv[1], O_RDONLY, 0644); xdup2(fd, 0); mysh(1, &myenv); xclose(fd); } else fprintf(stderr, "%s %s\n", "Unable to access :", argv[1]); } else mysh(!isatty(0), &myenv); free_env(myenv); auto_comp(NULL, NULL, DEL, NULL); return (EXIT_SUCCESS); }
int parse_command_for_pipe(char **command, int tab_len, int first) { int p[2]; int pid; int ret; if (xpipe(p) < 0) return (0); if ((pid = xfork()) > 0) { ret = exec_father(command[tab_len - 1], p, pid, first); if (ret > 0) return (ret); } else { xclose(p[0]); xdup2(p[1], 1); if (tab_len > 2) parse_command_for_pipe(command, tab_len - 1, 0); else parse_command_redir(command[0]); exit(0); } return (pid); }
// "Renumber" opened fd void FAST_FUNC xmove_fd(int from, int to) { if (from == to) return; xdup2(from, to); close(from); }
/* ** Redirects the pipe ** Has to be called in a child process ** mode == 1 Dup for i + 1 instruc ** mode == 0 Dup for i - 1 instruc */ void my_dup2(int mode, int toyos[2], int olds[2], int (*redir)()) { if (mode) { if (redir == red_left) xdup2(toyos[0], olds[0]); else xdup2(toyos[0], 0); } else { if (redir == red_left) xdup2(toyos[0], 0); xdup2(olds[1], 1); } }
int time_main(int argc UNUSED_PARAM, char **argv) { resource_t res; const char *output_format = default_format; int opt; opt_complementary = "-1"; /* at least one arg */ /* "+": stop on first non-option */ opt = getopt32(argv, "+vp"); argv += optind; if (opt & 1) output_format = long_format; if (opt & 2) output_format = posix_format; run_command(argv, &res); /* Cheat. printf's are shorter :) */ /* (but see bb_putchar() body for additional wrinkle!) */ xdup2(2, 1); /* just in case libc does something silly :( */ stdout = stderr; summarize(output_format, argv, &res); if (WIFSTOPPED(res.waitstatus)) return WSTOPSIG(res.waitstatus); if (WIFSIGNALED(res.waitstatus)) return WTERMSIG(res.waitstatus); if (WIFEXITED(res.waitstatus)) return WEXITSTATUS(res.waitstatus); fflush_stdout_and_exit(EXIT_SUCCESS); }
int time_main(int argc UNUSED_PARAM, char **argv) { resource_t res; const char *output_format = default_format; int opt; opt_complementary = "-1"; /* at least one arg */ /* "+": stop on first non-option */ opt = getopt32(argv, "+vp"); argv += optind; if (opt & 1) output_format = long_format; if (opt & 2) output_format = posix_format; run_command(argv, &res); /* Cheat. printf's are shorter :) */ xdup2(STDERR_FILENO, STDOUT_FILENO); summarize(output_format, argv, &res); if (WIFSTOPPED(res.waitstatus)) return WSTOPSIG(res.waitstatus); if (WIFSIGNALED(res.waitstatus)) return WTERMSIG(res.waitstatus); if (WIFEXITED(res.waitstatus)) return WEXITSTATUS(res.waitstatus); fflush_stdout_and_exit(EXIT_SUCCESS); }
int sys_exec_command (char *file_name, int typechar, struct tar_stat_info *st) { int p[2]; char *argv[4]; xpipe (p); pipe_handler = signal (SIGPIPE, SIG_IGN); global_pid = xfork (); if (global_pid != 0) { xclose (p[PREAD]); return p[PWRITE]; } /* Child */ xdup2 (p[PREAD], STDIN_FILENO); xclose (p[PWRITE]); stat_to_env (file_name, typechar, st); argv[0] = "/bin/sh"; argv[1] = "-c"; argv[2] = to_command_option; argv[3] = NULL; execv ("/bin/sh", argv); exec_fatal (file_name); }
void list_passiv(struct_client *clt) { int oldfd; int nsock; printf("150 Opening BINARY mode data connection for 'ls'\n"); if ((nsock = accept_data_client(clt)) == -1) { printf("520 LIST impossible to reach client\n"); return ; } oldfd = xdup(1); xdup2(nsock, 1); system("ls -la"); xdup2(oldfd, 1); printf("226 Transfer complete\n"); close_data_chan(clt, nsock); }
int exec_father(char *command, int *p, int pid, int first) { int ret; xclose(p[1]); if (first) ret = xdup(0); if (first && ret < 0) return (pid); if (xdup2(p[0], 0) < 0) return (pid); parse_command_redir(command); xwaitpid(pid, 0, (first ? WNOHANG : WNOHANG & WUNTRACED)); if (first && xdup2(ret, 0) < 0) return (pid); xclose(p[0]); xclose(ret); return (0); }
int openvt_main(int argc, char **argv) { char vtname[sizeof(VC_FORMAT) + 2]; if (argc < 3) bb_show_usage(); /* check for illegal vt number: < 1 or > 63 */ sprintf(vtname, VC_FORMAT, (int)xatou_range(argv[1], 1, 63)); bb_daemonize_or_rexec(DAEMON_CLOSE_EXTRA_FDS, argv); /* grab new one */ close(0); xopen(vtname, O_RDWR); xdup2(0, STDOUT_FILENO); xdup2(0, STDERR_FILENO); argv += 2; BB_EXECVP(argv[0], argv); _exit(1); }
int execute_last_command(t_tree *tree, t_shell *st_shell) { int statut; pid_t pid; if (is_builtin(tree->args[0])) return (check_builtins(st_shell, tree)); if ((pid = xfork()) == 0) { xdup2(tree->fd_in, 0); xdup2(tree->fd_out, 1); xexecve(tree->full_path, tree->args, st_shell->my_env); } else if (pid > 0) { if (waitpid(pid, &statut, 0) == -1) perror("Waitpid() :"); else return (write_statut(statut)); } return (FATAL_ERROR); }
int dcopy(int i, int j) { if (i == j || i < 0 || (j < 0 && i > FSAFE)) return (i); if (j >= 0) { #ifdef HAVE_DUP2 (void) xdup2(i, j); return (j); #else xclose(j); #endif } return (renum(i, j)); }
/* * Move descriptor i to j. * If j is -1 then we just want to get i to a safe place, * i.e. to a unit > FSAFE. This also happens in dcopy. */ int dmove(int i, int j) { if (i == j || i < 0) return (i); #ifdef HAVE_DUP2 if (j >= 0) { (void) xdup2(i, j); if (j != i) xclose(i); return (j); } #endif j = dcopy(i, j); if (j != i) xclose(i); return (j); }
int sys_exec_command (char *file_name, int typechar, struct tar_stat_info *st) { int p[2]; xpipe (p); pipe_handler = signal (SIGPIPE, SIG_IGN); global_pid = xfork (); if (global_pid != 0) { xclose (p[PREAD]); return p[PWRITE]; } /* Child */ xdup2 (p[PREAD], STDIN_FILENO); xclose (p[PWRITE]); stat_to_env (file_name, typechar, st); priv_set_restore_linkdir (); xexec (to_command_option); }
int gere_left_next(t_info *info, char *str, int flag) { char *file; char buff[BUFF_COMPL]; int fd; int value; if ((file = cdnarn(str, buff, "<")) == NULL) return (status(info, EXIT_FAILURE)); if ((fd = xopen(file, O_RDONLY)) == -1) { xfree(file); return (status(info, EXIT_FAILURE)); } xfree(file); if (xdup2(fd, 0) == EXIT_FAILURE) { xclose(fd); return (status(info, EXIT_FAILURE)); } value = gere(info, buff, flag); xclose(fd); return (value); }
int sendmail_main(int argc UNUSED_PARAM, char **argv) { char *opt_connect = opt_connect; char *opt_from = NULL; char *s; llist_t *list = NULL; char *host = sane_address(safe_gethostname()); unsigned nheaders = 0; int code; enum { HDR_OTHER = 0, HDR_TOCC, HDR_BCC, } last_hdr = 0; int check_hdr; int has_to = 0; enum { //--- standard options OPT_t = 1 << 0, // read message for recipients, append them to those on cmdline OPT_f = 1 << 1, // sender address OPT_o = 1 << 2, // various options. -oi IMPLIED! others are IGNORED! OPT_i = 1 << 3, // IMPLIED! //--- BB specific options OPT_w = 1 << 4, // network timeout OPT_H = 1 << 5, // use external connection helper OPT_S = 1 << 6, // specify connection string OPT_a = 1 << 7, // authentication tokens OPT_v = 1 << 8, // verbosity }; // init global variables INIT_G(); // save initial stdin since body is piped! xdup2(STDIN_FILENO, 3); G.fp0 = xfdopen_for_read(3); // parse options // -v is a counter, -H and -S are mutually exclusive, -a is a list opt_complementary = "vv:w+:H--S:S--H:a::"; // N.B. since -H and -S are mutually exclusive they do not interfere in opt_connect // -a is for ssmtp (http://downloads.openwrt.org/people/nico/man/man8/ssmtp.8.html) compatibility, // it is still under development. opts = getopt32(argv, "tf:o:iw:H:S:a::v", &opt_from, NULL, &timeout, &opt_connect, &opt_connect, &list, &verbose); //argc -= optind; argv += optind; // process -a[upm]<token> options if ((opts & OPT_a) && !list) bb_show_usage(); while (list) { char *a = (char *) llist_pop(&list); if ('u' == a[0]) G.user = xstrdup(a+1); if ('p' == a[0]) G.pass = xstrdup(a+1); // N.B. we support only AUTH LOGIN so far //if ('m' == a[0]) // G.method = xstrdup(a+1); } // N.B. list == NULL here //bb_info_msg("OPT[%x] AU[%s], AP[%s], AM[%s], ARGV[%s]", opts, au, ap, am, *argv); // connect to server // connection helper ordered? -> if (opts & OPT_H) { const char *args[] = { "sh", "-c", opt_connect, NULL }; // plug it in launch_helper(args); // Now: // our stdout will go to helper's stdin, // helper's stdout will be available on our stdin. // Wait for initial server message. // If helper (such as openssl) invokes STARTTLS, the initial 220 // is swallowed by helper (and not repeated after TLS is initiated). // We will send NOOP cmd to server and check the response. // We should get 220+250 on plain connection, 250 on STARTTLSed session. // // The problem here is some servers delay initial 220 message, // and consider client to be a spammer if it starts sending cmds // before 220 reached it. The code below is unsafe in this regard: // in non-STARTTLSed case, we potentially send NOOP before 220 // is sent by server. // Ideas? (--delay SECS opt? --assume-starttls-helper opt?) code = smtp_check("NOOP", -1); if (code == 220) // we got 220 - this is not STARTTLSed connection, // eat 250 response to our NOOP smtp_check(NULL, 250); else if (code != 250) bb_error_msg_and_die("SMTP init failed"); } else { // vanilla connection int fd; // host[:port] not explicitly specified? -> use $SMTPHOST // no $SMTPHOST? -> use localhost if (!(opts & OPT_S)) { opt_connect = getenv("SMTPHOST"); if (!opt_connect) opt_connect = (char *)"127.0.0.1"; } // do connect fd = create_and_connect_stream_or_die(opt_connect, 25); // and make ourselves a simple IO filter xmove_fd(fd, STDIN_FILENO); xdup2(STDIN_FILENO, STDOUT_FILENO); // Wait for initial server 220 message smtp_check(NULL, 220); } // we should start with modern EHLO if (250 != smtp_checkp("EHLO %s", host, -1)) smtp_checkp("HELO %s", host, 250); // perform authentication if (opts & OPT_a) { smtp_check("AUTH LOGIN", 334); // we must read credentials unless they are given via -a[up] options if (!G.user || !G.pass) get_cred_or_die(4); encode_base64(NULL, G.user, NULL); smtp_check("", 334); encode_base64(NULL, G.pass, NULL); smtp_check("", 235); } // set sender // N.B. we have here a very loosely defined algorythm // since sendmail historically offers no means to specify secrets on cmdline. // 1) server can require no authentication -> // we must just provide a (possibly fake) reply address. // 2) server can require AUTH -> // we must provide valid username and password along with a (possibly fake) reply address. // For the sake of security username and password are to be read either from console or from a secured file. // Since reading from console may defeat usability, the solution is either to read from a predefined // file descriptor (e.g. 4), or again from a secured file. // got no sender address? use auth name, then UID username as a last resort if (!opt_from) { opt_from = xasprintf("%s@%s", G.user ? G.user : xuid2uname(getuid()), xgethostbyname(host)->h_name); } free(host); smtp_checkp("MAIL FROM:<%s>", opt_from, 250); // process message // read recipients from message and add them to those given on cmdline. // this means we scan stdin for To:, Cc:, Bcc: lines until an empty line // and then use the rest of stdin as message body code = 0; // set "analyze headers" mode while ((s = xmalloc_fgetline(G.fp0)) != NULL) { dump: // put message lines doubling leading dots if (code) { // escape leading dots // N.B. this feature is implied even if no -i (-oi) switch given // N.B. we need to escape the leading dot regardless of // whether it is single or not character on the line if ('.' == s[0] /*&& '\0' == s[1] */) bb_putchar('.'); // dump read line send_r_n(s); free(s); continue; } // analyze headers // To: or Cc: headers add recipients check_hdr = (0 == strncasecmp("To:", s, 3)); has_to |= check_hdr; if (opts & OPT_t) { if (check_hdr || 0 == strncasecmp("Bcc:" + 1, s, 3)) { rcptto_list(s+3); last_hdr = HDR_TOCC; goto addheader; } // Bcc: header adds blind copy (hidden) recipient if (0 == strncasecmp("Bcc:", s, 4)) { rcptto_list(s+4); free(s); last_hdr = HDR_BCC; continue; // N.B. Bcc: vanishes from headers! } } check_hdr = (list && isspace(s[0])); if (strchr(s, ':') || check_hdr) { // other headers go verbatim // N.B. RFC2822 2.2.3 "Long Header Fields" allows for headers to occupy several lines. // Continuation is denoted by prefixing additional lines with whitespace(s). // Thanks (stefan.seyfried at googlemail.com) for pointing this out. if (check_hdr && last_hdr != HDR_OTHER) { rcptto_list(s+1); if (last_hdr == HDR_BCC) continue; // N.B. Bcc: vanishes from headers! } else { last_hdr = HDR_OTHER; } addheader: // N.B. we allow MAX_HEADERS generic headers at most to prevent attacks if (MAX_HEADERS && ++nheaders >= MAX_HEADERS) goto bail; llist_add_to_end(&list, s); } else { // a line without ":" (an empty line too, by definition) doesn't look like a valid header // so stop "analyze headers" mode reenter: // put recipients specified on cmdline check_hdr = 1; while (*argv) { char *t = sane_address(*argv); rcptto(t); //if (MAX_HEADERS && ++nheaders >= MAX_HEADERS) // goto bail; if (!has_to) { const char *hdr; if (check_hdr && argv[1]) hdr = "To: %s,"; else if (check_hdr) hdr = "To: %s"; else if (argv[1]) hdr = "To: %s," + 3; else hdr = "To: %s" + 3; llist_add_to_end(&list, xasprintf(hdr, t)); check_hdr = 0; } argv++; } // enter "put message" mode // N.B. DATA fails iff no recipients were accepted (or even provided) // in this case just bail out gracefully if (354 != smtp_check("DATA", -1)) goto bail; // dump the headers while (list) { send_r_n((char *) llist_pop(&list)); } // stop analyzing headers code++; // N.B. !s means: we read nothing, and nothing to be read in the future. // just dump empty line and break the loop if (!s) { send_r_n(""); break; } // go dump message body // N.B. "s" already contains the first non-header line, so pretend we read it from input goto dump; } } // odd case: we didn't stop "analyze headers" mode -> message body is empty. Reenter the loop // N.B. after reenter code will be > 0 if (!code) goto reenter; // finalize the message smtp_check(".", 250); bail: // ... and say goodbye smtp_check("QUIT", 221); // cleanup if (ENABLE_FEATURE_CLEAN_UP) fclose(G.fp0); return EXIT_SUCCESS; }
static int parse(const char *boundary, char **argv) { char *line, *s, *p; const char *type; int boundary_len = strlen(boundary); const char *delims = " ;\"\t\r\n"; const char *uniq; int ntokens; const char *tokens[32]; // 32 is enough // prepare unique string pattern uniq = xasprintf("%%llu.%u.%s", (unsigned)getpid(), safe_gethostname()); //bb_info_msg("PARSE[%s]", terminator); while ((line = xmalloc_fgets_str(stdin, "\r\n\r\n")) != NULL) { // seek to start of MIME section // N.B. to avoid false positives let us seek to the _last_ occurance p = NULL; s = line; while ((s=strcasestr(s, "Content-Type:")) != NULL) p = s++; if (!p) goto next; //bb_info_msg("L[%s]", p); // split to tokens // TODO: strip of comments which are of form: (comment-text) ntokens = 0; tokens[ntokens] = NULL; for (s = strtok(p, delims); s; s = strtok(NULL, delims)) { tokens[ntokens] = s; if (ntokens < ARRAY_SIZE(tokens) - 1) ntokens++; //bb_info_msg("L[%d][%s]", ntokens, s); } tokens[ntokens] = NULL; //bb_info_msg("N[%d]", ntokens); // analyse tokens type = find_token(tokens, "Content-Type:", "text/plain"); //bb_info_msg("T[%s]", type); if (0 == strncasecmp(type, "multipart/", 10)) { if (0 == strcasecmp(type+10, "mixed")) { parse(xfind_token(tokens, "boundary="), argv); } else bb_error_msg_and_die("no support of content type '%s'", type); } else { pid_t pid = pid; int rc; FILE *fp; // fetch charset const char *charset = find_token(tokens, "charset=", CONFIG_FEATURE_MIME_CHARSET); // fetch encoding const char *encoding = find_token(tokens, "Content-Transfer-Encoding:", "7bit"); // compose target filename char *filename = (char *)find_token(tokens, "filename=", NULL); if (!filename) filename = xasprintf(uniq, monotonic_us()); else filename = bb_get_last_path_component_strip(xstrdup(filename)); // start external helper, if any if (opts & OPT_X) { int fd[2]; xpipe(fd); pid = vfork(); if (0 == pid) { // child reads from fd[0] xdup2(fd[0], STDIN_FILENO); close(fd[0]); close(fd[1]); xsetenv("CONTENT_TYPE", type); xsetenv("CHARSET", charset); xsetenv("ENCODING", encoding); xsetenv("FILENAME", filename); BB_EXECVP(*argv, argv); _exit(EXIT_FAILURE); } // parent dumps to fd[1] close(fd[0]); fp = fdopen(fd[1], "w"); signal(SIGPIPE, SIG_IGN); // ignore EPIPE // or create a file for dump } else { char *fname = xasprintf("%s%s", *argv, filename); fp = xfopen_for_write(fname); free(fname); } // housekeeping free(filename); // dump to fp if (0 == strcasecmp(encoding, "base64")) { decode_base64(stdin, fp); } else if (0 != strcasecmp(encoding, "7bit") && 0 != strcasecmp(encoding, "8bit")) { // quoted-printable, binary, user-defined are unsupported so far bb_error_msg_and_die("no support of encoding '%s'", encoding); } else { // N.B. we have written redundant \n. so truncate the file // The following weird 2-tacts reading technique is due to // we have to not write extra \n at the end of the file // In case of -x option we could truncate the resulting file as // fseek(fp, -1, SEEK_END); // if (ftruncate(fileno(fp), ftell(fp))) // bb_perror_msg("ftruncate"); // But in case of -X we have to be much more careful. There is // no means to truncate what we already have sent to the helper. p = xmalloc_fgets_str(stdin, "\r\n"); while (p) { if ((s = xmalloc_fgets_str(stdin, "\r\n")) == NULL) break; if ('-' == s[0] && '-' == s[1] && 0 == strncmp(s+2, boundary, boundary_len)) break; fputs(p, fp); p = s; } /* while ((s = xmalloc_fgetline_str(stdin, "\r\n")) != NULL) { if ('-' == s[0] && '-' == s[1] && 0 == strncmp(s+2, boundary, boundary_len)) break; fprintf(fp, "%s\n", s); } // N.B. we have written redundant \n. so truncate the file fseek(fp, -1, SEEK_END); if (ftruncate(fileno(fp), ftell(fp))) bb_perror_msg("ftruncate"); */ } fclose(fp); // finalize helper if (opts & OPT_X) { signal(SIGPIPE, SIG_DFL); // exit if helper exited >0 rc = wait4pid(pid); if (rc) return rc+20; } // check multipart finalized if (s && '-' == s[2+boundary_len] && '-' == s[2+boundary_len+1]) { free(line); break; } } next: free(line); } //bb_info_msg("ENDPARSE[%s]", boundary); return EXIT_SUCCESS; }
/* Returns pid */ pid_t fork_execv_on_steroids(int flags, char **argv, int *pipefds, char **env_vec, const char *dir, uid_t uid) { pid_t child; /* Reminder: [0] is read end, [1] is write end */ int pipe_to_child[2]; int pipe_fm_child[2]; /* Sanitize flags */ if (!pipefds) flags &= ~(EXECFLG_INPUT | EXECFLG_OUTPUT); if (flags & EXECFLG_INPUT) xpipe(pipe_to_child); if (flags & EXECFLG_OUTPUT) xpipe(pipe_fm_child); fflush(NULL); child = fork(); if (child == -1) { perror_msg_and_die("fork"); } if (child == 0) { /* Child */ if (dir) xchdir(dir); if (flags & EXECFLG_SETGUID) { struct passwd* pw = getpwuid(uid); gid_t gid = pw ? pw->pw_gid : uid; setgroups(1, &gid); xsetregid(gid, gid); xsetreuid(uid, uid); } if (env_vec) { /* Note: we use the glibc extension that putenv("var") * *unsets* $var if "var" string has no '=' */ while (*env_vec) putenv(*env_vec++); } /* Play with stdio descriptors */ if (flags & EXECFLG_INPUT) { xmove_fd(pipe_to_child[0], STDIN_FILENO); close(pipe_to_child[1]); } else if (flags & EXECFLG_INPUT_NUL) { xmove_fd(xopen("/dev/null", O_RDWR), STDIN_FILENO); } if (flags & EXECFLG_OUTPUT) { xmove_fd(pipe_fm_child[1], STDOUT_FILENO); close(pipe_fm_child[0]); } else if (flags & EXECFLG_OUTPUT_NUL) { xmove_fd(xopen("/dev/null", O_RDWR), STDOUT_FILENO); } /* This should be done BEFORE stderr redirect */ VERB1 { char *r = concat_str_vector(argv); log("Executing: %s", r); free(r); } if (flags & EXECFLG_ERR2OUT) { /* Want parent to see errors in the same stream */ xdup2(STDOUT_FILENO, STDERR_FILENO); } else if (flags & EXECFLG_ERR_NUL) { xmove_fd(xopen("/dev/null", O_RDWR), STDERR_FILENO); } if (flags & EXECFLG_SETSID) setsid(); execvp(argv[0], argv); if (!(flags & EXECFLG_QUIET)) perror_msg("Can't execute '%s'", argv[0]); exit(127); /* shell uses this exit code in this case */ } if (flags & EXECFLG_INPUT) { close(pipe_to_child[0]); pipefds[1] = pipe_to_child[1]; } if (flags & EXECFLG_OUTPUT) { close(pipe_fm_child[1]); pipefds[0] = pipe_fm_child[0]; } return child; }
int lpd_main(int argc UNUSED_PARAM, char *argv[]) { int spooling = spooling; // for compiler char *s, *queue; char *filenames[2]; // goto spool directory if (*++argv) xchdir(*argv++); // error messages of xfuncs will be sent over network xdup2(STDOUT_FILENO, STDERR_FILENO); // nullify ctrl/data filenames memset(filenames, 0, sizeof(filenames)); // read command s = queue = xmalloc_read_stdin(); // we understand only "receive job" command if (2 != *queue) { unsupported_cmd: printf("Command %02x %s\n", (unsigned char)s[0], "is not supported"); goto err_exit; } // parse command: "2 | QUEUE_NAME | '\n'" queue++; // protect against "/../" attacks // *strchrnul(queue, '\n') = '\0'; - redundant, sane() will do if (!*sane(queue)) return EXIT_FAILURE; // queue is a directory -> chdir to it and enter spooling mode spooling = chdir(queue) + 1; // 0: cannot chdir, 1: done // we don't free(s), we might need "queue" var later while (1) { char *fname; int fd; // int is easier than ssize_t: can use xatoi_u, // and can correctly display error returns (-1) int expected_len, real_len; // signal OK safe_write(STDOUT_FILENO, "", 1); // get subcommand // valid s must be of form: "SUBCMD | LEN | space | FNAME" // N.B. we bail out on any error s = xmalloc_read_stdin(); if (!s) { // (probably) EOF char *p, *q, var[2]; // non-spooling mode or no spool helper specified if (!spooling || !*argv) return EXIT_SUCCESS; // the only non-error exit // spooling mode but we didn't see both ctrlfile & datafile if (spooling != 7) goto err_exit; // reject job // spooling mode and spool helper specified -> exec spool helper // (we exit 127 if helper cannot be executed) var[1] = '\0'; // read and delete ctrlfile q = xmalloc_xopen_read_close(filenames[0], NULL); unlink(filenames[0]); // provide datafile name // we can use leaky setenv since we are about to exec or exit xsetenv("DATAFILE", filenames[1]); // parse control file by "\n" while ((p = strchr(q, '\n')) != NULL && isalpha(*q)) { *p++ = '\0'; // q is a line of <SYM><VALUE>, // we are setting environment string <SYM>=<VALUE>. // Ignoring "l<datafile>", exporting others: if (*q != 'l') { var[0] = *q++; xsetenv(var, q); } q = p; // next line } // helper should not talk over network. // this call reopens stdio fds to "/dev/null" // (no daemonization is done) bb_daemonize_or_rexec(DAEMON_DEVNULL_STDIO | DAEMON_ONLY_SANITIZE, NULL); BB_EXECVP(*argv, argv); exit(127); } // validate input. // we understand only "control file" or "data file" cmds if (2 != s[0] && 3 != s[0]) goto unsupported_cmd; if (spooling & (1 << (s[0]-1))) { printf("Duplicated subcommand\n"); goto err_exit; } // get filename *strchrnul(s, '\n') = '\0'; fname = strchr(s, ' '); if (!fname) { // bad_fname: printf("No or bad filename\n"); goto err_exit; } *fname++ = '\0'; // // s[0]==2: ctrlfile, must start with 'c' // // s[0]==3: datafile, must start with 'd' // if (fname[0] != s[0] + ('c'-2)) // goto bad_fname; // get length expected_len = bb_strtou(s + 1, NULL, 10); if (errno || expected_len < 0) { printf("Bad length\n"); goto err_exit; } if (2 == s[0] && expected_len > 16 * 1024) { // SECURITY: // ctrlfile can't be big (we want to read it back later!) printf("File is too big\n"); goto err_exit; } // open the file if (spooling) { // spooling mode: dump both files // job in flight has mode 0200 "only writable" sane(fname); fd = open3_or_warn(fname, O_CREAT | O_WRONLY | O_TRUNC | O_EXCL, 0200); if (fd < 0) goto err_exit; filenames[s[0] - 2] = xstrdup(fname); } else { // non-spooling mode: // 2: control file (ignoring), 3: data file fd = -1; if (3 == s[0]) fd = xopen(queue, O_RDWR | O_APPEND); } // signal OK safe_write(STDOUT_FILENO, "", 1); // copy the file real_len = bb_copyfd_size(STDIN_FILENO, fd, expected_len); if (real_len != expected_len) { printf("Expected %d but got %d bytes\n", expected_len, real_len); goto err_exit; } // get EOF indicator, see whether it is NUL (ok) // (and don't trash s[0]!) if (safe_read(STDIN_FILENO, &s[1], 1) != 1 || s[1] != 0) { // don't send error msg to peer - it obviously // doesn't follow the protocol, so probably // it can't understand us either goto err_exit; } if (spooling) { // chmod completely downloaded file as "readable+writable" fchmod(fd, 0600); // accumulate dump state // N.B. after all files are dumped spooling should be 1+2+4==7 spooling |= (1 << (s[0]-1)); // bit 1: ctrlfile; bit 2: datafile } free(s); close(fd); // NB: can do close(-1). Who cares? // NB: don't do "signal OK" write here, it will be done // at the top of the loop } // while (1) err_exit: // don't keep corrupted files if (spooling) { #define i spooling for (i = 2; --i >= 0; ) if (filenames[i]) unlink(filenames[i]); } return EXIT_FAILURE; }
int vlock_main(int argc UNUSED_PARAM, char **argv) { #ifdef __linux__ struct vt_mode vtm; struct vt_mode ovtm; #endif struct termios term; struct termios oterm; struct passwd *pw; pw = xgetpwuid(getuid()); opt_complementary = "=0"; /* no params! */ getopt32(argv, "a"); /* Ignore some signals so that we don't get killed by them */ bb_signals(0 + (1 << SIGTSTP) + (1 << SIGTTIN) + (1 << SIGTTOU) + (1 << SIGHUP ) + (1 << SIGCHLD) /* paranoia :) */ + (1 << SIGQUIT) + (1 << SIGINT ) , SIG_IGN); #ifdef __linux__ /* We will use SIGUSRx for console switch control: */ /* 1: set handlers */ signal_SA_RESTART_empty_mask(SIGUSR1, release_vt); signal_SA_RESTART_empty_mask(SIGUSR2, acquire_vt); /* 2: unmask them */ sig_unblock(SIGUSR1); sig_unblock(SIGUSR2); #endif /* Revert stdin/out to our controlling tty * (or die if we have none) */ xmove_fd(xopen(CURRENT_TTY, O_RDWR), STDIN_FILENO); xdup2(STDIN_FILENO, STDOUT_FILENO); #ifdef __linux__ xioctl(STDIN_FILENO, VT_GETMODE, &vtm); ovtm = vtm; /* "console switches are controlled by us, not kernel!" */ vtm.mode = VT_PROCESS; vtm.relsig = SIGUSR1; vtm.acqsig = SIGUSR2; ioctl(STDIN_FILENO, VT_SETMODE, &vtm); #endif //TODO: use set_termios_to_raw() tcgetattr(STDIN_FILENO, &oterm); term = oterm; term.c_iflag |= IGNBRK; /* ignore serial break (why? VTs don't have breaks, right?) */ term.c_iflag &= ~BRKINT; /* redundant? "dont translate break to SIGINT" */ term.c_lflag &= ~(ISIG | ECHO | ECHOCTL); /* ignore ^C ^Z, echo off */ tcsetattr_stdin_TCSANOW(&term); while (1) { printf("Virtual console%s locked by %s.\n", /* "s" if -a, else "": */ "s" + !option_mask32, pw->pw_name ); if (ask_and_check_password(pw) > 0) { break; } bb_do_delay(LOGIN_FAIL_DELAY); puts("Incorrect password"); } #ifdef __linux__ ioctl(STDIN_FILENO, VT_SETMODE, &ovtm); #endif tcsetattr_stdin_TCSANOW(&oterm); fflush_stdout_and_exit(EXIT_SUCCESS); }
/* Create a new problem directory from client session. * Caller must ensure that all fields in struct client * are properly filled. */ static int create_problem_dir(GHashTable *problem_info, unsigned pid) { /* Exit if free space is less than 1/4 of MaxCrashReportsSize */ if (g_settings_nMaxCrashReportsSize > 0) { if (low_free_space(g_settings_nMaxCrashReportsSize, g_settings_dump_location)) exit(1); } /* Create temp directory with the problem data. * This directory is renamed to final directory name after * all files have been stored into it. */ gchar *dir_basename = g_hash_table_lookup(problem_info, "basename"); if (!dir_basename) dir_basename = g_hash_table_lookup(problem_info, FILENAME_TYPE); char *path = xasprintf("%s/%s-%s-%u.new", g_settings_dump_location, dir_basename, iso_date_string(NULL), pid); /* This item is useless, don't save it */ g_hash_table_remove(problem_info, "basename"); /* No need to check the path length, as all variables used are limited, * and dd_create() fails if the path is too long. */ struct dump_dir *dd = dd_create(path, /*fs owner*/0, DEFAULT_DUMP_DIR_MODE); if (!dd) { error_msg_and_die("Error creating problem directory '%s'", path); } dd_create_basic_files(dd, client_uid, NULL); dd_save_text(dd, FILENAME_ABRT_VERSION, VERSION); gpointer gpkey = g_hash_table_lookup(problem_info, FILENAME_CMDLINE); if (!gpkey) { /* Obtain and save the command line. */ char *cmdline = get_cmdline(pid); if (cmdline) { dd_save_text(dd, FILENAME_CMDLINE, cmdline); free(cmdline); } } /* Store id of the user whose application crashed. */ char uid_str[sizeof(long) * 3 + 2]; sprintf(uid_str, "%lu", (long)client_uid); dd_save_text(dd, FILENAME_UID, uid_str); GHashTableIter iter; gpointer gpvalue; g_hash_table_iter_init(&iter, problem_info); while (g_hash_table_iter_next(&iter, &gpkey, &gpvalue)) { dd_save_text(dd, (gchar *) gpkey, (gchar *) gpvalue); } dd_close(dd); /* Not needing it anymore */ g_hash_table_destroy(problem_info); /* Move the completely created problem directory * to final directory. */ char *newpath = xstrndup(path, strlen(path) - strlen(".new")); if (rename(path, newpath) == 0) strcpy(path, newpath); free(newpath); log_notice("Saved problem directory of pid %u to '%s'", pid, path); /* We let the peer know that problem dir was created successfully * _before_ we run potentially long-running post-create. */ printf("HTTP/1.1 201 Created\r\n\r\n"); fflush(NULL); close(STDOUT_FILENO); xdup2(STDERR_FILENO, STDOUT_FILENO); /* paranoia: don't leave stdout fd closed */ /* Trim old problem directories if necessary */ if (g_settings_nMaxCrashReportsSize > 0) { trim_problem_dirs(g_settings_dump_location, g_settings_nMaxCrashReportsSize * (double)(1024*1024), path); } run_post_create(path); /* free(path); */ exit(0); }
static void startservice(struct svdir *s) { int p; const char *arg[4]; char exitcode[sizeof(int)*3 + 2]; if (s->state == S_FINISH) { /* Two arguments are given to ./finish. The first one is ./run exit code, * or -1 if ./run didnt exit normally. The second one is * the least significant byte of the exit status as determined by waitpid; * for instance it is 0 if ./run exited normally, and the signal number * if ./run was terminated by a signal. If runsv cannot start ./run * for some reason, the exit code is 111 and the status is 0. */ arg[0] = "./finish"; arg[1] = "-1"; if (WIFEXITED(s->wstat)) { *utoa_to_buf(WEXITSTATUS(s->wstat), exitcode, sizeof(exitcode)) = '\0'; arg[1] = exitcode; } //arg[2] = "0"; //if (WIFSIGNALED(s->wstat)) { arg[2] = utoa(WTERMSIG(s->wstat)); //} arg[3] = NULL; } else { arg[0] = "./run"; arg[1] = NULL; custom(s, 'u'); } if (s->pid != 0) stopservice(s); /* should never happen */ while ((p = vfork()) == -1) { warn_cannot("vfork, sleeping"); sleep(5); } if (p == 0) { /* child */ if (haslog) { /* NB: bug alert! right order is close, then dup2 */ if (s->islog) { xchdir("./log"); close(logpipe.wr); xdup2(logpipe.rd, 0); } else { close(logpipe.rd); xdup2(logpipe.wr, 1); } } /* Non-ignored signals revert to SIG_DFL on exec anyway */ /*bb_signals(0 + (1 << SIGCHLD) + (1 << SIGTERM) , SIG_DFL);*/ sig_unblock(SIGCHLD); sig_unblock(SIGTERM); execv(arg[0], (char**) arg); fatal2_cannot(s->islog ? "start log/" : "start ", arg[0]); } /* parent */ if (s->state != S_FINISH) { gettimeofday_ns(&s->start); s->state = S_RUN; } s->pid = p; pidchanged = 1; s->ctrl = C_NOOP; update_status(s); }
void FAST_FUNC data_extract_to_command(archive_handle_t *archive_handle) { file_header_t *file_header = archive_handle->file_header; #if 0 /* do we need this? ENABLE_FEATURE_TAR_SELINUX */ char *sctx = archive_handle->tar__sctx[PAX_NEXT_FILE]; if (!sctx) sctx = archive_handle->tar__sctx[PAX_GLOBAL]; if (sctx) { /* setfscreatecon is 4 syscalls, avoid if possible */ setfscreatecon(sctx); free(archive_handle->tar__sctx[PAX_NEXT_FILE]); archive_handle->tar__sctx[PAX_NEXT_FILE] = NULL; } #endif if ((file_header->mode & S_IFMT) == S_IFREG) { pid_t pid; int p[2], status; char *tar_env[TAR_MAX]; memset(tar_env, 0, sizeof(tar_env)); xpipe(p); pid = BB_MMU ? xfork() : xvfork(); if (pid == 0) { /* Child */ /* str2env(tar_env, TAR_FILETYPE, "f"); - parent should do it once */ oct2env(tar_env, TAR_MODE, file_header->mode); str2env(tar_env, TAR_FILENAME, file_header->name); str2env(tar_env, TAR_REALNAME, file_header->name); #if ENABLE_FEATURE_TAR_UNAME_GNAME str2env(tar_env, TAR_UNAME, file_header->tar__uname); str2env(tar_env, TAR_GNAME, file_header->tar__gname); #endif dec2env(tar_env, TAR_SIZE, file_header->size); dec2env(tar_env, TAR_UID, file_header->uid); dec2env(tar_env, TAR_GID, file_header->gid); close(p[1]); xdup2(p[0], STDIN_FILENO); signal(SIGPIPE, SIG_DFL); execl(archive_handle->tar__to_command_shell, archive_handle->tar__to_command_shell, "-c", archive_handle->tar__to_command, NULL); bb_perror_msg_and_die("can't execute '%s'", archive_handle->tar__to_command_shell); } close(p[0]); /* Our caller is expected to do signal(SIGPIPE, SIG_IGN) * so that we don't die if child don't read all the input: */ bb_copyfd_exact_size(archive_handle->src_fd, p[1], -file_header->size); close(p[1]); if (safe_waitpid(pid, &status, 0) == -1) bb_perror_msg_and_die("waitpid"); if (WIFEXITED(status) && WEXITSTATUS(status)) bb_error_msg_and_die("'%s' returned status %d", archive_handle->tar__to_command, WEXITSTATUS(status)); if (WIFSIGNALED(status)) bb_error_msg_and_die("'%s' terminated on signal %d", archive_handle->tar__to_command, WTERMSIG(status)); if (!BB_MMU) { int i; for (i = 0; i < TAR_MAX; i++) { if (tar_env[i]) bb_unsetenv_and_free(tar_env[i]); } } } #if 0 /* ENABLE_FEATURE_TAR_SELINUX */ if (sctx) /* reset the context after creating an entry */ setfscreatecon(NULL); #endif }