void set_global_stop_on_not_reportable(bool enabled, int flags/*unused - persistent*/) { assert_global_configuration_initialized(); if (enabled) xsetenv(STOP_ON_NOT_REPORTABLE, "1"); else xsetenv(STOP_ON_NOT_REPORTABLE, "0"); }
static void euidgid(char *user) { struct bb_uidgid_t ugid; if (!get_uidgid(&ugid, user, 1)) { bb_error_msg_and_die("unknown user/group: %s", user); } xsetenv("GID", utoa(ugid.gid)); xsetenv("UID", utoa(ugid.uid)); }
/* Setting munin specific vars */ static void setenvvars_system() { /* Some locales use "," as decimal separator. * This can mess up a lot of plugins. */ xsetenv("LC_ALL", "C", yes); /* LC_ALL should be enough, but some plugins don't * follow specs (#1014) */ xsetenv("LANG", "C", yes); /* PATH should be *very* sane by default. Can be * overrided via config file if needed * (Closes #863 and #1128). */ xsetenv("PATH", "/usr/sbin:/usr/bin:/sbin:/bin", yes); }
static void change_user(const struct passwd *pas) { xsetenv("USER", pas->pw_name); xsetenv("HOME", pas->pw_dir); xsetenv("SHELL", DEFAULT_SHELL); /* initgroups, setgid, setuid */ change_identity(pas); if (chdir(pas->pw_dir) < 0) { bb_perror_msg("chdir(%s) by %s failed", pas->pw_dir, pas->pw_name); xchdir("/tmp"); } }
// main takes care of OS-specific startup and dispatches to xmain. void main(int argc, char **argv) { Buf b; setvbuf(stdout, nil, _IOLBF, BUFSIZ); setvbuf(stderr, nil, _IOLBF, BUFSIZ); binit(&b); rfork(RFENVG); slash = "/"; gohostos = "plan9"; xgetenv(&b, "objtype"); if(b.len == 0) fatal("$objtype is unset"); gohostarch = btake(&b); xgetenv(&b, "GOBIN"); if(b.len == 0){ bpathf(&b, "/%s/bin", gohostarch); xsetenv("GOBIN", bstr(&b)); } srand(time(0)+getpid()); init(); xmain(argc, argv); bfree(&b); exits(nil); }
/** * Run a script. * argv[0]:intf argv[1]:script_name argv[2]:junk argv[3]:NULL */ static int run(char *argv[3], const char *param, struct in_addr *ip) { int status; char *addr = addr; /* for gcc */ const char *fmt = "%s %s %s" + 3; argv[2] = (char*)param; VDBG("%s run %s %s\n", argv[0], argv[1], argv[2]); if (ip) { addr = inet_ntoa(*ip); xsetenv("ip", addr); fmt -= 3; } if (verbose) bb_info_msg(fmt, argv[2], argv[0], addr); status = spawn_and_wait(argv + 1); if (status < 0) { bb_perror_msg("%s %s %s" + 3, argv[2], argv[0]); return -errno; } if (status != 0) bb_error_msg("script %s %s failed, exitcode=%d", argv[1], argv[2], status & 0xff); return status; }
/* TODO: use recursive_action? */ static NOINLINE void edir(const char *directory_name) { int wdir; DIR *dir; struct dirent *d; int fd; wdir = xopen(".", O_RDONLY | O_NDELAY); xchdir(directory_name); dir = xopendir("."); for (;;) { char buf[256]; char *tail; int size; errno = 0; d = readdir(dir); if (!d) { if (errno) bb_perror_msg_and_die("readdir %s", directory_name); break; } if (d->d_name[0] == '.') continue; fd = open(d->d_name, O_RDONLY | O_NDELAY); if (fd < 0) { if ((errno == EISDIR) && directory_name) { if (option_mask32 & OPT_v) bb_perror_msg("warning: %s/%s is a directory", directory_name, d->d_name); continue; } bb_perror_msg_and_die("open %s/%s", directory_name, d->d_name); } size = full_read(fd, buf, sizeof(buf)-1); close(fd); if (size < 0) bb_perror_msg_and_die("read %s/%s", directory_name, d->d_name); if (size == 0) { unsetenv(d->d_name); continue; } buf[size] = '\n'; tail = strchr(buf, '\n'); /* skip trailing whitespace */ while (1) { *tail = '\0'; tail--; if (tail < buf || !isspace(*tail)) break; } xsetenv(d->d_name, buf); } closedir(dir); xfchdir(wdir); close(wdir); }
static void set_env_vars(struct passwd *pas, const char *shell) { /* POSIX requires crond to set up at least HOME, LOGNAME, PATH, SHELL. * We assume crond inherited suitable PATH. */ #if SETENV_LEAKS safe_setenv(&G.env_var_logname, "LOGNAME", pas->pw_name); safe_setenv(&G.env_var_user, "USER", pas->pw_name); safe_setenv(&G.env_var_home, "HOME", pas->pw_dir); safe_setenv(&G.env_var_shell, "SHELL", shell); #else xsetenv("LOGNAME", pas->pw_name); xsetenv("USER", pas->pw_name); xsetenv("HOME", pas->pw_dir); xsetenv("SHELL", shell); #endif }
/* Setting munin specific vars */ static void setenvvars_munin() { /* munin-node will override this with the IP of the * connecting master */ if (client_ip != NULL && client_ip[0] != '\0') { xsetenv("MUNIN_MASTER_IP", client_ip, no); } /* Tell plugins about supported capabilities */ xsetenv("MUNIN_CAP_MULTIGRAPH", "1", no); /* We only have one user, so using a fixed path */ xsetenv("MUNIN_PLUGSTATE", "/var/tmp", no); xsetenv("MUNIN_STATEFILE", "/dev/null", no); /* That's where plugins should live */ xsetenv("MUNIN_LIBDIR", "/usr/share/munin", no); }
void set_global_create_private_ticket(bool enabled, int flags/*unused - persistent*/) { assert_global_configuration_initialized(); if (enabled) xsetenv(CREATE_PRIVATE_TICKET, "1"); else safe_unsetenv(CREATE_PRIVATE_TICKET); }
void setup_environment ( const char *shell, int loginshell, int changeenv, const struct passwd *pw ) { if ( loginshell ) { const char *term; /* Change the current working directory to be the home directory * of the user. It is a fatal error for this process to be unable * to change to that directory. There is no "default" home * directory. * Some systems default to HOME=/ */ if ( chdir ( pw-> pw_dir )) { if ( chdir ( "/" )) { syslog ( LOG_WARNING, "unable to cd to %s' for user %s'\n", pw-> pw_dir, pw-> pw_name ); bb_error_msg_and_die ( "cannot cd to home directory or /" ); } fputs ( "warning: cannot change to home directory\n", stderr ); } /* Leave TERM unchanged. Set HOME, SHELL, USER, LOGNAME, PATH. Unset all other environment variables. */ term = getenv ("TERM"); clearenv ( ); if ( term ) xsetenv ( "TERM", term ); xsetenv ( "HOME", pw-> pw_dir ); xsetenv ( "SHELL", shell ); xsetenv ( "USER", pw-> pw_name ); xsetenv ( "LOGNAME", pw-> pw_name ); xsetenv ( "PATH", ( pw-> pw_uid ? DEFAULT_LOGIN_PATH : DEFAULT_ROOT_LOGIN_PATH )); } else if ( changeenv ) { /* Set HOME, SHELL, and if not becoming a super-user, USER and LOGNAME. */ xsetenv ( "HOME", pw-> pw_dir ); xsetenv ( "SHELL", shell ); if ( pw-> pw_uid ) { xsetenv ( "USER", pw-> pw_name ); xsetenv ( "LOGNAME", pw-> pw_name ); } } }
/* Restore CLASSPATH to its previous value. */ void reset_classpath (char *old_classpath) { if (old_classpath != NULL) { xsetenv (CLASSPATHVAR, old_classpath, 1); free (old_classpath); } else unsetenv (CLASSPATHVAR); }
static void run_login_script(struct passwd *pw, char *full_tty) { char *t_argv[2]; t_argv[0] = getenv("LOGIN_PRE_SUID_SCRIPT"); if (t_argv[0]) { t_argv[1] = NULL; xsetenv("LOGIN_TTY", full_tty); xsetenv("LOGIN_USER", pw->pw_name); xsetenv("LOGIN_UID", utoa(pw->pw_uid)); xsetenv("LOGIN_GID", utoa(pw->pw_gid)); xsetenv("LOGIN_SHELL", pw->pw_shell); spawn_and_wait(t_argv); /* NOMMU-friendly */ unsetenv("LOGIN_TTY"); unsetenv("LOGIN_USER"); unsetenv("LOGIN_UID"); unsetenv("LOGIN_GID"); unsetenv("LOGIN_SHELL"); } }
int main (int argc, char *argv[]) { char *s; int result = 0; /* Clean up environment. */ unsetenv ("LANGUAGE"); unsetenv ("LC_ALL"); unsetenv ("LC_MESSAGES"); unsetenv ("LC_CTYPE"); unsetenv ("LANG"); unsetenv ("OUTPUT_CHARSET"); xsetenv ("LC_ALL", argv[1], 1); setlocale (LC_ALL, ""); textdomain ("codeset"); bindtextdomain ("codeset", "gt-4"); /* Here we expect output in ISO-8859-1. Except on Darwin 7 or newer and on BeOS, for which locale_charset () always returns "UTF-8" (see config.charset). */ #if !((defined __APPLE__ && defined __MACH__) || defined __BEOS__) s = gettext ("cheese"); if (strcmp (s, "K\344se")) { fprintf (stderr, "call 1 returned: %s\n", s); result = 1; } #endif bind_textdomain_codeset ("codeset", "UTF-8"); /* Here we expect output in UTF-8. */ s = gettext ("cheese"); if (strcmp (s, "K\303\244se")) { fprintf (stderr, "call 2 returned: %s\n", s); result = 1; } bind_textdomain_codeset ("codeset", "ISO-8859-1"); /* Here we expect output in ISO-8859-1. */ s = gettext ("cheese"); if (strcmp (s, "K\344se")) { fprintf (stderr, "call 3 returned: %s\n", s); result = 1; } return result; }
static void modify_environment (const struct passwd *pw, const char *shell) { if (simulate_login) { /* Leave TERM unchanged. Set HOME, SHELL, USER, LOGNAME, PATH. Unset all other environment variables. */ char *term = getenv ("TERM"); if (term) term = xstrdup (term); environ = xmalloc ((6 + !!term) * sizeof (char *)); environ[0] = NULL; if (term) { xsetenv ("TERM", term, 1); free(term); } xsetenv ("HOME", pw->pw_dir, 1); if (shell) xsetenv ("SHELL", shell, 1); xsetenv ("USER", pw->pw_name, 1); xsetenv ("LOGNAME", pw->pw_name, 1); set_path(pw); } else { /* Set HOME, SHELL, and (if not becoming a superuser) USER and LOGNAME. */ if (change_environment) { xsetenv ("HOME", pw->pw_dir, 1); if (shell) xsetenv ("SHELL", shell, 1); if (getlogindefs_bool ("ALWAYS_SET_PATH", 0)) set_path(pw); if (pw->pw_uid) { xsetenv ("USER", pw->pw_name, 1); xsetenv ("LOGNAME", pw->pw_name, 1); } } } export_pamenv (); }
// The bootstrap command runs a build from scratch, // stopping at having installed the go_bootstrap command. void cmdbootstrap(int argc, char **argv) { int i; Buf b; char *oldgoos, *oldgoarch, *oldgochar; binit(&b); ARGBEGIN{ case 'a': rebuildall = 1; break; case 'v': vflag++; break; default: usage(); }ARGEND if(argc > 0) usage(); if(rebuildall) clean(); goversion = findgoversion(); setup(); xsetenv("GOROOT", goroot); xsetenv("GOROOT_FINAL", goroot_final); // For the main bootstrap, building for host os/arch. oldgoos = goos; oldgoarch = goarch; oldgochar = gochar; goos = gohostos; goarch = gohostarch; gochar = gohostchar; xsetenv("GOARCH", goarch); xsetenv("GOOS", goos); for(i=0; i<nelem(buildorder); i++) { install(bprintf(&b, buildorder[i], gohostchar)); if(!streq(oldgochar, gohostchar) && xstrstr(buildorder[i], "%s")) install(bprintf(&b, buildorder[i], oldgochar)); } goos = oldgoos; goarch = oldgoarch; gochar = oldgochar; xsetenv("GOARCH", goarch); xsetenv("GOOS", goos); // Build pkg/runtime for actual goos/goarch too. if(!streq(goos, gohostos) || !streq(goarch, gohostarch)) install("pkg/runtime"); bfree(&b); }
int main (int argc, char *argv[]) { int n = atoi (argv[2]); xsetenv ("LC_ALL", argv[1], 1); if (setlocale (LC_ALL, "") == NULL) return 1; textdomain ("cake"); bindtextdomain ("cake", "plural-1-dir"); printf (ngettext ("a piece of cake", "%d pieces of cake", n), n); printf ("\n"); return 0; }
void make_run_event_state_forwarding(struct run_event_state *state) { /* reset callbacks, just to be sure */ state->alert_callback = run_event_stdio_alert; state->ask_callback = run_event_stdio_ask; state->ask_yes_no_callback = run_event_stdio_ask_yes_no; state->ask_yes_no_yesforever_callback= run_event_stdio_ask_yes_no_yesforever; state->ask_password_callback = run_event_stdio_ask_password; /* * Not sure if we should reset even logging_callback and error_callback? */ xsetenv("REPORT_CLIENT_SLAVE", "1"); }
int main (int argc, char *argv[]) { int n = 5; const char *en; const char *s; const char *expected_translation; const char *expected_result; char buf[100]; xsetenv ("LC_ALL", argv[1], 1); if (setlocale (LC_ALL, "") == NULL) { fprintf (stderr, "Couldn't set locale.\n"); exit (1); } textdomain ("fc5"); bindtextdomain ("fc5", "."); s = gettext ("father of %d children"); en = "father of %d children"; #if __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 2) expected_translation = "Vater von %Id Kindern"; expected_result = "Vater von \xdb\xb5 Kindern"; #else expected_translation = "Vater von %d Kindern"; expected_result = "Vater von 5 Kindern"; #endif if (strcmp (s, en) == 0) { fprintf (stderr, "String untranslated.\n"); exit (1); } if (strcmp (s, expected_translation) != 0) { fprintf (stderr, "String incorrectly translated.\n"); exit (1); } sprintf (buf, s, n); if (strcmp (buf, expected_result) != 0) { fprintf (stderr, "printf of translation wrong.\n"); exit (1); } return 0; }
/* Set CLASSPATH and returns a safe copy of its old value. */ char * set_classpath (const char * const *classpaths, unsigned int classpaths_count, bool use_minimal_classpath, bool verbose) { const char *old_CLASSPATH = getenv (CLASSPATHVAR); char *result = (old_CLASSPATH != NULL ? xstrdup (old_CLASSPATH) : NULL); char *new_CLASSPATH = new_classpath (classpaths, classpaths_count, use_minimal_classpath); if (verbose) printf (CLASSPATHVAR "=%s ", new_CLASSPATH); xsetenv (CLASSPATHVAR, new_CLASSPATH, 1); free (new_CLASSPATH); return result; }
int main (int argc, char *argv[]) { unsigned char n = 5; const char *s; const char *c1; const char *c2; char buf[100]; xsetenv ("LC_ALL", argv[1], 1); if (setlocale (LC_ALL, "") == NULL) { fprintf (stderr, "Couldn't set locale.\n"); exit (1); } textdomain ("fc4"); bindtextdomain ("fc4", "."); s = ngettext ("father of %"PRId8" child", "father of %"PRId8" children", n); c1 = "Vater von %"; c2 = " Kindern"; if (!(strlen (s) > strlen (c1) + strlen (c2) && memcmp (s, c1, strlen (c1)) == 0 && memcmp (s + strlen (s) - strlen (c2), c2, strlen (c2)) == 0)) { fprintf (stderr, "String not translated.\n"); exit (1); } if (strchr (s, '<') != NULL || strchr (s, '>') != NULL) { fprintf (stderr, "Translation contains <...> markers.\n"); exit (1); } sprintf (buf, s, n); if (strcmp (buf, "Vater von 5 Kindern") != 0) { fprintf (stderr, "printf of translation wrong.\n"); exit (1); } return 0; }
/* * Initialize $TERM, $HOME, ... */ static void init_environ(struct login_context *cxt) { struct passwd *pwd = cxt->pwd; char *termenv, **env; char tmp[PATH_MAX]; int len, i; termenv = getenv("TERM"); if (termenv) termenv = xstrdup(termenv); /* destroy environment unless user has requested preservation (-p) */ if (!cxt->keep_env) { environ = xmalloc(sizeof(char *)); memset(environ, 0, sizeof(char *)); } xsetenv("HOME", pwd->pw_dir, 0); /* legal to override */ xsetenv("USER", pwd->pw_name, 1); xsetenv("SHELL", pwd->pw_shell, 1); xsetenv("TERM", termenv ? termenv : "dumb", 1); free(termenv); if (pwd->pw_uid) { if (logindefs_setenv("PATH", "ENV_PATH", _PATH_DEFPATH) != 0) err(EXIT_FAILURE, _("failed to set the %s environment variable"), "PATH"); } else if (logindefs_setenv("PATH", "ENV_ROOTPATH", NULL) != 0 && logindefs_setenv("PATH", "ENV_SUPATH", _PATH_DEFPATH_ROOT) != 0) { err(EXIT_FAILURE, _("failed to set the %s environment variable"), "PATH"); } /* mailx will give a funny error msg if you forget this one */ len = snprintf(tmp, sizeof(tmp), "%s/%s", _PATH_MAILDIR, pwd->pw_name); if (len > 0 && (size_t) len < sizeof(tmp)) xsetenv("MAIL", tmp, 0); /* LOGNAME is not documented in login(1) but HP-UX 6.5 does it. We'll * not allow modifying it. */ xsetenv("LOGNAME", pwd->pw_name, 1); env = pam_getenvlist(cxt->pamh); for (i = 0; env && env[i]; i++) putenv(env[i]); }
void FAST_FUNC setup_environment(const char *shell, int flags, const struct passwd *pw) { if (!shell || !shell[0]) shell = DEFAULT_SHELL; /* Change the current working directory to be the home directory * of the user */ if (!(flags & SETUP_ENV_NO_CHDIR)) { if (chdir(pw->pw_dir) != 0) { bb_error_msg("can't change directory to '%s'", pw->pw_dir); xchdir((flags & SETUP_ENV_TO_TMP) ? "/tmp" : "/"); } } if (flags & SETUP_ENV_CLEARENV) { const char *term; /* Leave TERM unchanged. Set HOME, SHELL, USER, LOGNAME, PATH. * Unset all other environment variables. */ term = getenv("TERM"); clearenv(); if (term) xsetenv("TERM", term); xsetenv("PATH", (pw->pw_uid ? bb_default_path : bb_default_root_path)); goto shortcut; // No, gcc (4.2.1) is not clever enougn to do it itself. //xsetenv("USER", pw->pw_name); //xsetenv("LOGNAME", pw->pw_name); //xsetenv("HOME", pw->pw_dir); //xsetenv("SHELL", shell); } else if (flags & SETUP_ENV_CHANGEENV) { #if ENABLE_PLATFORM_MINGW32 #define xsetenv(k, v) xsetenv_if_unset(k, v) #endif /* Set HOME, SHELL, and if not becoming a super-user, * USER and LOGNAME. */ if (pw->pw_uid) { shortcut: xsetenv("USER", pw->pw_name); xsetenv("LOGNAME", pw->pw_name); } xsetenv("HOME", pw->pw_dir); xsetenv("SHELL", shell); } }
void FAST_FUNC setup_environment(const char *shell, int clear_env, int change_env, const struct passwd *pw) { /* Change the current working directory to be the home directory * of the user */ if (chdir(pw->pw_dir)) { xchdir("/"); bb_error_msg("can't chdir to home directory '%s'", pw->pw_dir); } if (clear_env) { const char *term; /* Leave TERM unchanged. Set HOME, SHELL, USER, LOGNAME, PATH. Unset all other environment variables. */ term = getenv("TERM"); clearenv(); if (term) xsetenv("TERM", term); xsetenv("PATH", (pw->pw_uid ? bb_default_path : bb_default_root_path)); goto shortcut; // No, gcc (4.2.1) is not clever enougn to do it itself. //xsetenv("USER", pw->pw_name); //xsetenv("LOGNAME", pw->pw_name); //xsetenv("HOME", pw->pw_dir); //xsetenv("SHELL", shell); } else if (change_env) { /* Set HOME, SHELL, and if not becoming a super-user, USER and LOGNAME. */ if (pw->pw_uid) { shortcut: xsetenv("USER", pw->pw_name); xsetenv("LOGNAME", pw->pw_name); } xsetenv("HOME", pw->pw_dir); xsetenv("SHELL", shell); } }
int zcip_main(int argc UNUSED_PARAM, char **argv) { int state; char *r_opt; unsigned opts; // ugly trick, but I want these zeroed in one go struct { const struct in_addr null_ip; const struct ether_addr null_addr; struct in_addr ip; struct ifreq ifr; int timeout_ms; /* must be signed */ unsigned conflicts; unsigned nprobes; unsigned nclaims; int ready; } L; #define null_ip (L.null_ip ) #define null_addr (L.null_addr ) #define ip (L.ip ) #define ifr (L.ifr ) #define timeout_ms (L.timeout_ms) #define conflicts (L.conflicts ) #define nprobes (L.nprobes ) #define nclaims (L.nclaims ) #define ready (L.ready ) memset(&L, 0, sizeof(L)); INIT_G(); #define FOREGROUND (opts & 1) #define QUIT (opts & 2) // parse commandline: prog [options] ifname script // exactly 2 args; -v accumulates and implies -f opt_complementary = "=2:vv:vf"; opts = getopt32(argv, "fqr:p:v", &r_opt, &pidfile, &verbose); #if !BB_MMU // on NOMMU reexec early (or else we will rerun things twice) if (!FOREGROUND) bb_daemonize_or_rexec(0 /*was: DAEMON_CHDIR_ROOT*/, argv); #endif // open an ARP socket // (need to do it before openlog to prevent openlog from taking // fd 3 (sock_fd==3)) xmove_fd(xsocket(AF_PACKET, SOCK_PACKET, htons(ETH_P_ARP)), sock_fd); if (!FOREGROUND) { // do it before all bb_xx_msg calls openlog(applet_name, 0, LOG_DAEMON); logmode |= LOGMODE_SYSLOG; } if (opts & 4) { // -r n.n.n.n if (inet_aton(r_opt, &ip) == 0 || (ntohl(ip.s_addr) & IN_CLASSB_NET) != LINKLOCAL_ADDR ) { bb_error_msg_and_die("invalid link address"); } } argv += optind - 1; /* Now: argv[0]:junk argv[1]:intf argv[2]:script argv[3]:NULL */ /* We need to make space for script argument: */ argv[0] = argv[1]; argv[1] = argv[2]; /* Now: argv[0]:intf argv[1]:script argv[2]:junk argv[3]:NULL */ #define argv_intf (argv[0]) xsetenv("interface", argv_intf); // initialize the interface (modprobe, ifup, etc) if (run(argv, "init", NULL)) return EXIT_FAILURE; // initialize saddr // saddr is: { u16 sa_family; u8 sa_data[14]; } //memset(&saddr, 0, sizeof(saddr)); //TODO: are we leaving sa_family == 0 (AF_UNSPEC)?! safe_strncpy(saddr.sa_data, argv_intf, sizeof(saddr.sa_data)); // bind to the interface's ARP socket xbind(sock_fd, &saddr, sizeof(saddr)); // get the interface's ethernet address //memset(&ifr, 0, sizeof(ifr)); strncpy_IFNAMSIZ(ifr.ifr_name, argv_intf); xioctl(sock_fd, SIOCGIFHWADDR, &ifr); memcpy(ð_addr, &ifr.ifr_hwaddr.sa_data, ETH_ALEN); // start with some stable ip address, either a function of // the hardware address or else the last address we used. // we are taking low-order four bytes, as top-order ones // aren't random enough. // NOTE: the sequence of addresses we try changes only // depending on when we detect conflicts. { uint32_t t; move_from_unaligned32(t, ((char *)ð_addr + 2)); srand(t); } if (ip.s_addr == 0) ip.s_addr = pick(); // FIXME cases to handle: // - zcip already running! // - link already has local address... just defend/update // daemonize now; don't delay system startup if (!FOREGROUND) { #if BB_MMU bb_daemonize(0 /*was: DAEMON_CHDIR_ROOT*/); #endif if (verbose) bb_info_msg("start, interface %s", argv_intf); } write_pidfile(pidfile); bb_signals(BB_FATAL_SIGS, cleanup); // run the dynamic address negotiation protocol, // restarting after address conflicts: // - start with some address we want to try // - short random delay // - arp probes to see if another host uses it // - arp announcements that we're claiming it // - use it // - defend it, within limits // exit if: // - address is successfully obtained and -q was given: // run "<script> config", then exit with exitcode 0 // - poll error (when does this happen?) // - read error (when does this happen?) // - sendto error (in arp()) (when does this happen?) // - revents & POLLERR (link down). run "<script> deconfig" first state = PROBE; while (1) { struct pollfd fds[1]; unsigned deadline_us; struct arp_packet p; int source_ip_conflict; int target_ip_conflict; fds[0].fd = sock_fd; fds[0].events = POLLIN; fds[0].revents = 0; // poll, being ready to adjust current timeout if (!timeout_ms) { timeout_ms = random_delay_ms(PROBE_WAIT); // FIXME setsockopt(sock_fd, SO_ATTACH_FILTER, ...) to // make the kernel filter out all packets except // ones we'd care about. } // set deadline_us to the point in time when we timeout deadline_us = MONOTONIC_US() + timeout_ms * 1000; VDBG("...wait %d %s nprobes=%u, nclaims=%u\n", timeout_ms, argv_intf, nprobes, nclaims); switch (safe_poll(fds, 1, timeout_ms)) { default: //bb_perror_msg("poll"); - done in safe_poll cleanup(EXIT_FAILURE); // timeout case 0: VDBG("state = %d\n", state); switch (state) { case PROBE: // timeouts in the PROBE state mean no conflicting ARP packets // have been received, so we can progress through the states if (nprobes < PROBE_NUM) { nprobes++; VDBG("probe/%u %s@%s\n", nprobes, argv_intf, inet_ntoa(ip)); arp(/* ARPOP_REQUEST, */ /* ð_addr, */ null_ip, &null_addr, ip); timeout_ms = PROBE_MIN * 1000; timeout_ms += random_delay_ms(PROBE_MAX - PROBE_MIN); } else { // Switch to announce state. state = ANNOUNCE; nclaims = 0; VDBG("announce/%u %s@%s\n", nclaims, argv_intf, inet_ntoa(ip)); arp(/* ARPOP_REQUEST, */ /* ð_addr, */ ip, ð_addr, ip); timeout_ms = ANNOUNCE_INTERVAL * 1000; } break; case RATE_LIMIT_PROBE: // timeouts in the RATE_LIMIT_PROBE state mean no conflicting ARP packets // have been received, so we can move immediately to the announce state state = ANNOUNCE; nclaims = 0; VDBG("announce/%u %s@%s\n", nclaims, argv_intf, inet_ntoa(ip)); arp(/* ARPOP_REQUEST, */ /* ð_addr, */ ip, ð_addr, ip); timeout_ms = ANNOUNCE_INTERVAL * 1000; break; case ANNOUNCE: // timeouts in the ANNOUNCE state mean no conflicting ARP packets // have been received, so we can progress through the states if (nclaims < ANNOUNCE_NUM) { nclaims++; VDBG("announce/%u %s@%s\n", nclaims, argv_intf, inet_ntoa(ip)); arp(/* ARPOP_REQUEST, */ /* ð_addr, */ ip, ð_addr, ip); timeout_ms = ANNOUNCE_INTERVAL * 1000; } else { // Switch to monitor state. state = MONITOR; // link is ok to use earlier // FIXME update filters run(argv, "config", &ip); ready = 1; conflicts = 0; timeout_ms = -1; // Never timeout in the monitor state. // NOTE: all other exit paths // should deconfig ... if (QUIT) cleanup(EXIT_SUCCESS); } break; case DEFEND: // We won! No ARP replies, so just go back to monitor. state = MONITOR; timeout_ms = -1; conflicts = 0; break; default: // Invalid, should never happen. Restart the whole protocol. state = PROBE; ip.s_addr = pick(); timeout_ms = 0; nprobes = 0; nclaims = 0; break; } // switch (state) break; // case 0 (timeout) // packets arriving, or link went down case 1: // We need to adjust the timeout in case we didn't receive // a conflicting packet. if (timeout_ms > 0) { unsigned diff = deadline_us - MONOTONIC_US(); if ((int)(diff) < 0) { // Current time is greater than the expected timeout time. // Should never happen. VDBG("missed an expected timeout\n"); timeout_ms = 0; } else { VDBG("adjusting timeout\n"); timeout_ms = (diff / 1000) | 1; /* never 0 */ } } if ((fds[0].revents & POLLIN) == 0) { if (fds[0].revents & POLLERR) { // FIXME: links routinely go down; // this shouldn't necessarily exit. bb_error_msg("iface %s is down", argv_intf); if (ready) { run(argv, "deconfig", &ip); } cleanup(EXIT_FAILURE); } continue; } // read ARP packet if (safe_read(sock_fd, &p, sizeof(p)) < 0) { bb_perror_msg(bb_msg_read_error); cleanup(EXIT_FAILURE); } if (p.eth.ether_type != htons(ETHERTYPE_ARP)) continue; #ifdef DEBUG { struct ether_addr *sha = (struct ether_addr *) p.arp.arp_sha; struct ether_addr *tha = (struct ether_addr *) p.arp.arp_tha; struct in_addr *spa = (struct in_addr *) p.arp.arp_spa; struct in_addr *tpa = (struct in_addr *) p.arp.arp_tpa; VDBG("%s recv arp type=%d, op=%d,\n", argv_intf, ntohs(p.eth.ether_type), ntohs(p.arp.arp_op)); VDBG("\tsource=%s %s\n", ether_ntoa(sha), inet_ntoa(*spa)); VDBG("\ttarget=%s %s\n", ether_ntoa(tha), inet_ntoa(*tpa)); } #endif if (p.arp.arp_op != htons(ARPOP_REQUEST) && p.arp.arp_op != htons(ARPOP_REPLY)) continue; source_ip_conflict = 0; target_ip_conflict = 0; if (memcmp(p.arp.arp_spa, &ip.s_addr, sizeof(struct in_addr)) == 0 && memcmp(&p.arp.arp_sha, ð_addr, ETH_ALEN) != 0 ) { source_ip_conflict = 1; } if (p.arp.arp_op == htons(ARPOP_REQUEST) && memcmp(p.arp.arp_tpa, &ip.s_addr, sizeof(struct in_addr)) == 0 && memcmp(&p.arp.arp_tha, ð_addr, ETH_ALEN) != 0 ) { target_ip_conflict = 1; } VDBG("state = %d, source ip conflict = %d, target ip conflict = %d\n", state, source_ip_conflict, target_ip_conflict); switch (state) { case PROBE: case ANNOUNCE: // When probing or announcing, check for source IP conflicts // and other hosts doing ARP probes (target IP conflicts). if (source_ip_conflict || target_ip_conflict) { conflicts++; if (conflicts >= MAX_CONFLICTS) { VDBG("%s ratelimit\n", argv_intf); timeout_ms = RATE_LIMIT_INTERVAL * 1000; state = RATE_LIMIT_PROBE; } // restart the whole protocol ip.s_addr = pick(); timeout_ms = 0; nprobes = 0; nclaims = 0; } break; case MONITOR: // If a conflict, we try to defend with a single ARP probe. if (source_ip_conflict) { VDBG("monitor conflict -- defending\n"); state = DEFEND; timeout_ms = DEFEND_INTERVAL * 1000; arp(/* ARPOP_REQUEST, */ /* ð_addr, */ ip, ð_addr, ip); } break; case DEFEND: // Well, we tried. Start over (on conflict). if (source_ip_conflict) { state = PROBE; VDBG("defend conflict -- starting over\n"); ready = 0; run(argv, "deconfig", &ip); // restart the whole protocol ip.s_addr = pick(); timeout_ms = 0; nprobes = 0; nclaims = 0; } break; default: // Invalid, should never happen. Restart the whole protocol. VDBG("invalid state -- starting over\n"); state = PROBE; ip.s_addr = pick(); timeout_ms = 0; nprobes = 0; nclaims = 0; break; } // switch state break; // case 1 (packets arriving) } // switch poll } // while (1) #undef argv_intf }
bool execute_java_class (const char *class_name, const char * const *classpaths, unsigned int classpaths_count, bool use_minimal_classpath, const char *exe_dir, const char * const *args, bool verbose, bool quiet, execute_fn *executer, void *private_data) { bool err = false; unsigned int nargs; char *old_JAVA_HOME; /* Count args. */ { const char * const *arg; for (nargs = 0, arg = args; *arg != NULL; nargs++, arg++) ; } /* First, try a class compiled to a native code executable. */ if (exe_dir != NULL) { char *exe_pathname = concatenated_filename (exe_dir, class_name, EXEEXT); char *old_classpath; char **argv = (char **) xmalloca ((1 + nargs + 1) * sizeof (char *)); unsigned int i; /* Set CLASSPATH. */ old_classpath = set_classpath (classpaths, classpaths_count, use_minimal_classpath, verbose); argv[0] = exe_pathname; for (i = 0; i <= nargs; i++) argv[1 + i] = (char *) args[i]; if (verbose) { char *command = shell_quote_argv (argv); printf ("%s\n", command); free (command); } err = executer (class_name, exe_pathname, argv, private_data); /* Reset CLASSPATH. */ reset_classpath (old_classpath); freea (argv); goto done1; } { const char *java = getenv ("JAVA"); if (java != NULL && java[0] != '\0') { /* Because $JAVA may consist of a command and options, we use the shell. Because $JAVA has been set by the user, we leave all all environment variables in place, including JAVA_HOME, and we don't erase the user's CLASSPATH. */ char *old_classpath; unsigned int command_length; char *command; char *argv[4]; const char * const *arg; char *p; /* Set CLASSPATH. */ old_classpath = set_classpath (classpaths, classpaths_count, false, verbose); command_length = strlen (java); command_length += 1 + shell_quote_length (class_name); for (arg = args; *arg != NULL; arg++) command_length += 1 + shell_quote_length (*arg); command_length += 1; command = (char *) xmalloca (command_length); p = command; /* Don't shell_quote $JAVA, because it may consist of a command and options. */ memcpy (p, java, strlen (java)); p += strlen (java); *p++ = ' '; p = shell_quote_copy (p, class_name); for (arg = args; *arg != NULL; arg++) { *p++ = ' '; p = shell_quote_copy (p, *arg); } *p++ = '\0'; /* Ensure command_length was correctly calculated. */ if (p - command > command_length) abort (); if (verbose) printf ("%s\n", command); argv[0] = "/bin/sh"; argv[1] = "-c"; argv[2] = command; argv[3] = NULL; err = executer (java, "/bin/sh", argv, private_data); freea (command); /* Reset CLASSPATH. */ reset_classpath (old_classpath); goto done1; } } /* Unset the JAVA_HOME environment variable. */ old_JAVA_HOME = getenv ("JAVA_HOME"); if (old_JAVA_HOME != NULL) { old_JAVA_HOME = xstrdup (old_JAVA_HOME); unsetenv ("JAVA_HOME"); } { static bool gij_tested; static bool gij_present; if (!gij_tested) { /* Test for presence of gij: "gij --version > /dev/null" */ char *argv[3]; int exitstatus; argv[0] = "gij"; argv[1] = "--version"; argv[2] = NULL; exitstatus = execute ("gij", "gij", argv, false, false, true, true, true, false); gij_present = (exitstatus == 0); gij_tested = true; } if (gij_present) { char *old_classpath; char **argv = (char **) xmalloca ((2 + nargs + 1) * sizeof (char *)); unsigned int i; /* Set CLASSPATH. */ old_classpath = set_classpath (classpaths, classpaths_count, use_minimal_classpath, verbose); argv[0] = "gij"; argv[1] = (char *) class_name; for (i = 0; i <= nargs; i++) argv[2 + i] = (char *) args[i]; if (verbose) { char *command = shell_quote_argv (argv); printf ("%s\n", command); free (command); } err = executer ("gij", "gij", argv, private_data); /* Reset CLASSPATH. */ reset_classpath (old_classpath); freea (argv); goto done2; } } { static bool java_tested; static bool java_present; if (!java_tested) { /* Test for presence of java: "java -version 2> /dev/null" */ char *argv[3]; int exitstatus; argv[0] = "java"; argv[1] = "-version"; argv[2] = NULL; exitstatus = execute ("java", "java", argv, false, false, true, true, true, false); java_present = (exitstatus == 0); java_tested = true; } if (java_present) { char *old_classpath; char **argv = (char **) xmalloca ((2 + nargs + 1) * sizeof (char *)); unsigned int i; /* Set CLASSPATH. We don't use the "-classpath ..." option because in JDK 1.1.x its argument should also contain the JDK's classes.zip, but we don't know its location. (In JDK 1.3.0 it would work.) */ old_classpath = set_classpath (classpaths, classpaths_count, use_minimal_classpath, verbose); argv[0] = "java"; argv[1] = (char *) class_name; for (i = 0; i <= nargs; i++) argv[2 + i] = (char *) args[i]; if (verbose) { char *command = shell_quote_argv (argv); printf ("%s\n", command); free (command); } err = executer ("java", "java", argv, private_data); /* Reset CLASSPATH. */ reset_classpath (old_classpath); freea (argv); goto done2; } } { static bool jre_tested; static bool jre_present; if (!jre_tested) { /* Test for presence of jre: "jre 2> /dev/null ; test $? = 1" */ char *argv[2]; int exitstatus; argv[0] = "jre"; argv[1] = NULL; exitstatus = execute ("jre", "jre", argv, false, false, true, true, true, false); jre_present = (exitstatus == 0 || exitstatus == 1); jre_tested = true; } if (jre_present) { char *old_classpath; char **argv = (char **) xmalloca ((2 + nargs + 1) * sizeof (char *)); unsigned int i; /* Set CLASSPATH. We don't use the "-classpath ..." option because in JDK 1.1.x its argument should also contain the JDK's classes.zip, but we don't know its location. */ old_classpath = set_classpath (classpaths, classpaths_count, use_minimal_classpath, verbose); argv[0] = "jre"; argv[1] = (char *) class_name; for (i = 0; i <= nargs; i++) argv[2 + i] = (char *) args[i]; if (verbose) { char *command = shell_quote_argv (argv); printf ("%s\n", command); free (command); } err = executer ("jre", "jre", argv, private_data); /* Reset CLASSPATH. */ reset_classpath (old_classpath); freea (argv); goto done2; } } #if defined _WIN32 || defined __WIN32__ || defined __CYGWIN__ /* Win32, Cygwin */ { static bool jview_tested; static bool jview_present; if (!jview_tested) { /* Test for presence of jview: "jview -? >nul ; test $? = 1" */ char *argv[3]; int exitstatus; argv[0] = "jview"; argv[1] = "-?"; argv[2] = NULL; exitstatus = execute ("jview", "jview", argv, false, false, true, true, true, false); jview_present = (exitstatus == 0 || exitstatus == 1); jview_tested = true; } if (jview_present) { char *old_classpath; char **argv = (char **) xmalloca ((2 + nargs + 1) * sizeof (char *)); unsigned int i; /* Set CLASSPATH. */ old_classpath = set_classpath (classpaths, classpaths_count, use_minimal_classpath, verbose); argv[0] = "jview"; argv[1] = (char *) class_name; for (i = 0; i <= nargs; i++) argv[2 + i] = (char *) args[i]; if (verbose) { char *command = shell_quote_argv (argv); printf ("%s\n", command); free (command); } err = executer ("jview", "jview", argv, private_data); /* Reset CLASSPATH. */ reset_classpath (old_classpath); freea (argv); goto done2; } } #endif if (!quiet) error (0, 0, _("Java virtual machine not found, try installing gij or set $JAVA")); err = true; done2: if (old_JAVA_HOME != NULL) { xsetenv ("JAVA_HOME", old_JAVA_HOME, 1); free (old_JAVA_HOME); } done1: return err; }
// init handles initialization of the various global state, like goroot and goarch. void init(void) { char *p; int i; Buf b; binit(&b); xgetenv(&b, "GOROOT"); if(b.len > 0) { // if not "/", then strip trailing path separator if(b.len >= 2 && b.p[b.len - 1] == slash[0]) b.len--; goroot = btake(&b); } xgetenv(&b, "GOBIN"); if(b.len == 0) bprintf(&b, "%s%sbin", goroot, slash); gobin = btake(&b); xgetenv(&b, "GOOS"); if(b.len == 0) bwritestr(&b, gohostos); goos = btake(&b); if(find(goos, okgoos, nelem(okgoos)) < 0) fatal("unknown $GOOS %s", goos); xgetenv(&b, "GOARM"); if(b.len == 0) bwritestr(&b, xgetgoarm()); goarm = btake(&b); xgetenv(&b, "GO386"); if(b.len == 0) { if(cansse2()) bwritestr(&b, "sse2"); else bwritestr(&b, "387"); } go386 = btake(&b); p = bpathf(&b, "%s/include/u.h", goroot); if(!isfile(p)) { fatal("$GOROOT is not set correctly or not exported\n" "\tGOROOT=%s\n" "\t%s does not exist", goroot, p); } xgetenv(&b, "GOHOSTARCH"); if(b.len > 0) gohostarch = btake(&b); i = find(gohostarch, okgoarch, nelem(okgoarch)); if(i < 0) fatal("unknown $GOHOSTARCH %s", gohostarch); bprintf(&b, "%c", gochars[i]); gohostchar = btake(&b); xgetenv(&b, "GOARCH"); if(b.len == 0) bwritestr(&b, gohostarch); goarch = btake(&b); i = find(goarch, okgoarch, nelem(okgoarch)); if(i < 0) fatal("unknown $GOARCH %s", goarch); bprintf(&b, "%c", gochars[i]); gochar = btake(&b); xgetenv(&b, "GO_EXTLINK_ENABLED"); if(b.len > 0) { goextlinkenabled = btake(&b); if(!streq(goextlinkenabled, "0") && !streq(goextlinkenabled, "1")) fatal("unknown $GO_EXTLINK_ENABLED %s", goextlinkenabled); } xgetenv(&b, "CC"); if(b.len == 0) { // Use clang on OS X, because gcc is deprecated there. // Xcode for OS X 10.9 Mavericks will ship a fake "gcc" binary that // actually runs clang. We prepare different command // lines for the two binaries, so it matters what we call it. // See golang.org/issue/5822. if(defaultclang) bprintf(&b, "clang"); else bprintf(&b, "gcc"); } defaultcc = btake(&b); xgetenv(&b, "CFLAGS"); defaultcflags = btake(&b); xgetenv(&b, "LDFLAGS"); defaultldflags = btake(&b); xgetenv(&b, "CC_FOR_TARGET"); if(b.len == 0) { bprintf(&b, defaultcc); } defaultcctarget = btake(&b); xgetenv(&b, "CXX_FOR_TARGET"); if(b.len == 0) { xgetenv(&b, "CXX"); if(b.len == 0) { if(defaultclang) bprintf(&b, "clang++"); else bprintf(&b, "g++"); } } defaultcxxtarget = btake(&b); xsetenv("GOROOT", goroot); xsetenv("GOARCH", goarch); xsetenv("GOOS", goos); xsetenv("GOARM", goarm); xsetenv("GO386", go386); // Make the environment more predictable. xsetenv("LANG", "C"); xsetenv("LANGUAGE", "en_US.UTF8"); goversion = findgoversion(); workdir = xworkdir(); xatexit(rmworkdir); bpathf(&b, "%s/pkg/tool/%s_%s", goroot, gohostos, gohostarch); tooldir = btake(&b); bfree(&b); }
// init handles initialization of the various global state, like goroot and goarch. void init(void) { char *p; int i; Buf b; binit(&b); xgetenv(&b, "GOROOT"); if(b.len > 0) { // if not "/", then strip trailing path separator if(b.len >= 2 && b.p[b.len - 1] == slash[0]) b.len--; goroot = btake(&b); } xgetenv(&b, "GOBIN"); if(b.len == 0) bprintf(&b, "%s%sbin", goroot, slash); gobin = btake(&b); xgetenv(&b, "GOOS"); if(b.len == 0) bwritestr(&b, gohostos); goos = btake(&b); if(find(goos, okgoos, nelem(okgoos)) < 0) fatal("unknown $GOOS %s", goos); p = bpathf(&b, "%s/include/u.h", goroot); if(!isfile(p)) { fatal("$GOROOT is not set correctly or not exported\n" "\tGOROOT=%s\n" "\t%s does not exist", goroot, p); } xgetenv(&b, "GOHOSTARCH"); if(b.len > 0) gohostarch = btake(&b); i = find(gohostarch, okgoarch, nelem(okgoarch)); if(i < 0) fatal("unknown $GOHOSTARCH %s", gohostarch); bprintf(&b, "%c", gochars[i]); gohostchar = btake(&b); xgetenv(&b, "GOARCH"); if(b.len == 0) bwritestr(&b, gohostarch); goarch = btake(&b); i = find(goarch, okgoarch, nelem(okgoarch)); if(i < 0) fatal("unknown $GOARCH %s", goarch); bprintf(&b, "%c", gochars[i]); gochar = btake(&b); xsetenv("GOROOT", goroot); xsetenv("GOARCH", goarch); xsetenv("GOOS", goos); // Make the environment more predictable. xsetenv("LANG", "C"); xsetenv("LANGUAGE", "en_US.UTF8"); goversion = findgoversion(); workdir = xworkdir(); xatexit(rmworkdir); bpathf(&b, "%s/pkg/tool/%s_%s", goroot, gohostos, gohostarch); tooldir = btake(&b); bfree(&b); }
static void xsetenv_if_unset(const char *key, const char *value) { if (!getenv(key)) xsetenv(key, value); }
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; }