static const char *
safe(const char* s)
{
    int i, len, safe_flag = 1;
    char *t;

    i = 0;
    while(s[i] != '\0') {
        if(unsafe(s[i]))
            safe_flag = 0;
        i++;
    }

    if(safe_flag) return strdup(s);

    len = i;
    t = malloc(len + 1);
    if(t == NULL) {
        perror("Couldn't allocate string");
        exit(1);
    }

    for(i = 0; i < len; i++) {
        if(unsafe(s[i]))
            t[i] = ' ';
        else
            t[i] = s[i];
    }
    t[i] = '\0';
    return t;
}
Esempio n. 2
0
int main()
{
    MyDB db;
    Unsafe unsafe(&db);
    Exclusive excl(&unsafe);
    Pool pool(&excl);
    auto mngr = std::unique_ptr<Proxy>(new Proxy(&pool));
    (*mngr)->exec();
    return 0;
}
Esempio n. 3
0
/*
 * EXTRACT SENDER'S ADDRESS OUT OF RFC822 "FROM" LINE
 *  The sender is either the next first whitespace delimited token or
 *  the first thing enclosed in "<" ">".
 *  (leading "From: " is already deleted before entry)
 *  adapted from /n/bowell/src/cmd/upas (Dave Presotto)
 *  modified by Eric Grosse to stop after comma and to allow nested parens
 */
int   /* 0 on success, 1 if address is unparseable or unsafe */
getfrom(char *line, char *sender)
{
	char	*lp, *sp;
	int	comment = 0;
	int	anticomment = 0;

	sp = sender;
	for (lp = line; *lp; lp++) {
		if (comment) {
			if (*lp == ')') {
				comment--;
			} else if (*lp == '(') {
				comment++;
			}
			continue;
		}
		if (anticomment) {
			if (*lp == '>')
				break;
		}
		switch (*lp) {
		case '\t':
		case '\n':
			break;
		case ' ':
			if (strncmp(lp, " at ", sizeof(" at ") - 1) == 0 ||
				strncmp(lp, " AT ", sizeof(" AT ") - 1) == 0 ) {
				*sp++ = '@';
				lp += sizeof(" at ") - 2;
			}
			break;
		case '<':
			anticomment = 1;
			sp = sender;
			break;
		case '(':
			comment++;
			break;
		case ',':  /* looks like multiple address; chop */
			*sp++ = '\0';
		default:
			*sp++ = *lp;
			break;
		}
	}
	*sp = '\0';
	if(!*sender || unsafe(sender)) return 1;
	return 0;
}
Esempio n. 4
0
void    msg_syslog_init(const char *name, int logopt, int facility)
{
    static int first_call = 1;

    /*
     * XXX If this program is set-gid, then TZ must not be trusted. This
     * scrubbing code is in the wrong place.
     */
    if (unsafe())
	putenv("TZ=UTC");
    tzset();
    openlog(name, LOG_NDELAY | logopt, facility);
    if (first_call) {
	first_call = 0;
	msg_output(msg_syslog_print);
    }
}
Esempio n. 5
0
/*
 * Returns num of cycles to send and recv n doubles.
 */
double time_unsafe(int rank, int nprocs, int n){
	double* sendbuf;
	double* recvbuf;
	sendbuf = new double[n];
	recvbuf = new double[n];
	srand(0);
	for(int i=0; i < n; i++)
		sendbuf[i] = 1.0*rand()/RAND_MAX*(rank+1);
	
	TimeStamp clk; 
	clk.tic();
	unsafe(n, rank, nprocs, sendbuf, recvbuf);
	double cycles = clk.toc();
	
	delete[] sendbuf;
	delete[] recvbuf;

	return cycles;
}
Esempio n. 6
0
void    msg_syslog_init(const char *name, int logopt, int facility)
{
    static int first_call = 1;
    extern char **environ;

    /*
     * XXX If this program is set-gid, then TZ must not be trusted. This
     * scrubbing code is in the wrong place.
     */
    if (unsafe())
	while (getenv("TZ"))			/* There may be multiple. */
	    if (unsetenv("TZ") < 0) {		/* Desperate measures. */
		environ[0] = 0;
		msg_fatal("unsetenv: %m");
	    }
    tzset();
    openlog(name, LOG_NDELAY | logopt, facility);
    if (first_call) {
	first_call = 0;
	msg_output(msg_syslog_print);
    }
}
Esempio n. 7
0
int     main(int argc, char **argv)
{
    static VSTREAM *lock_fp;
    static VSTREAM *data_lock_fp;
    VSTRING *lock_path;
    VSTRING *data_lock_path;
    off_t   inherited_limit;
    int     debug_me = 0;
    int     ch;
    int     fd;
    int     n;
    int     test_lock = 0;
    VSTRING *why;
    WATCHDOG *watchdog;
    ARGV   *import_env;

    /*
     * Fingerprint executables and core dumps.
     */
    MAIL_VERSION_STAMP_ALLOCATE;

    /*
     * Initialize.
     */
    umask(077);					/* never fails! */

    /*
     * Process environment options as early as we can.
     */
    if (getenv(CONF_ENV_VERB))
	msg_verbose = 1;
    if (getenv(CONF_ENV_DEBUG))
	debug_me = 1;

    /*
     * Don't die when a process goes away unexpectedly.
     */
    signal(SIGPIPE, SIG_IGN);

    /*
     * Strip and save the process name for diagnostics etc.
     */
    var_procname = mystrdup(basename(argv[0]));

    /*
     * When running a child process, don't leak any open files that were
     * leaked to us by our own (privileged) parent process. Descriptors 0-2
     * are taken care of after we have initialized error logging.
     * 
     * Some systems such as AIX have a huge per-process open file limit. In
     * those cases, limit the search for potential file descriptor leaks to
     * just the first couple hundred.
     * 
     * The Debian post-installation script passes an open file descriptor into
     * the master process and waits forever for someone to close it. Because
     * of this we have to close descriptors > 2, and pray that doing so does
     * not break things.
     */
    closefrom(3);

    /*
     * Initialize logging and exit handler.
     */
    msg_syslog_init(mail_task(var_procname), LOG_PID, LOG_FACILITY);

    /*
     * Check the Postfix library version as soon as we enable logging.
     */
    MAIL_VERSION_CHECK;

    /*
     * The mail system must be run by the superuser so it can revoke
     * privileges for selected operations. That's right - it takes privileges
     * to toss privileges.
     */
    if (getuid() != 0)
	msg_fatal("the master command is reserved for the superuser");
    if (unsafe() != 0)
	msg_fatal("the master command must not run as a set-uid process");

    /*
     * Process JCL.
     */
    while ((ch = GETOPT(argc, argv, "c:Dde:tv")) > 0) {
	switch (ch) {
	case 'c':
	    if (setenv(CONF_ENV_PATH, optarg, 1) < 0)
		msg_fatal("out of memory");
	    break;
	case 'd':
	    master_detach = 0;
	    break;
	case 'e':
	    event_request_timer(master_exit_event, (char *) 0, atoi(optarg));
	    break;
	case 'D':
	    debug_me = 1;
	    break;
	case 't':
	    test_lock = 1;
	    break;
	case 'v':
	    msg_verbose++;
	    break;
	default:
	    usage(argv[0]);
	    /* NOTREACHED */
	}
    }

    /*
     * This program takes no other arguments.
     */
    if (argc > optind)
	usage(argv[0]);

    /*
     * If started from a terminal, get rid of any tty association. This also
     * means that all errors and warnings must go to the syslog daemon.
     */
    if (master_detach)
	for (fd = 0; fd < 3; fd++) {
	    (void) close(fd);
	    if (open("/dev/null", O_RDWR, 0) != fd)
		msg_fatal("open /dev/null: %m");
	}

    /*
     * Run in a separate process group, so that "postfix stop" can terminate
     * all MTA processes cleanly. Give up if we can't separate from our
     * parent process. We're not supposed to blow away the parent.
     */
    if (debug_me == 0 && master_detach != 0 && setsid() == -1 && getsid(0) != getpid())
	msg_fatal("unable to set session and process group ID: %m");

    /*
     * Make some room for plumbing with file descriptors. XXX This breaks
     * when a service listens on many ports. In order to do this right we
     * must change the master-child interface so that descriptors do not need
     * to have fixed numbers.
     * 
     * In a child we need two descriptors for the flow control pipe, one for
     * child->master status updates and at least one for listening.
     */
    for (n = 0; n < 5; n++) {
	if (close_on_exec(dup(0), CLOSE_ON_EXEC) < 0)
	    msg_fatal("dup(0): %m");
    }

    /*
     * Final initializations. Unfortunately, we must read the global Postfix
     * configuration file after doing command-line processing, so that we get
     * consistent results when we SIGHUP the server to reload configuration
     * files.
     */
    master_vars_init();

    /*
     * In case of multi-protocol support. This needs to be done because
     * master does not invoke mail_params_init() (it was written before that
     * code existed).
     */
    (void) inet_proto_init(VAR_INET_PROTOCOLS, var_inet_protocols);

    /*
     * Environment import filter, to enforce consistent behavior whether
     * Postfix is started by hand, or at system boot time.
     */
    import_env = argv_split(var_import_environ, ", \t\r\n");
    clean_env(import_env->argv);
    argv_free(import_env);

    if ((inherited_limit = get_file_limit()) < 0)
	set_file_limit(OFF_T_MAX);

    if (chdir(var_queue_dir))
	msg_fatal("chdir %s: %m", var_queue_dir);

    /*
     * Lock down the master.pid file. In test mode, no file means that it
     * isn't locked.
     */
    lock_path = vstring_alloc(10);
    data_lock_path = vstring_alloc(10);
    why = vstring_alloc(10);

    vstring_sprintf(lock_path, "%s/%s.pid", DEF_PID_DIR, var_procname);
    if (test_lock && access(vstring_str(lock_path), F_OK) < 0)
	exit(0);
    lock_fp = open_lock(vstring_str(lock_path), O_RDWR | O_CREAT, 0644, why);
    if (test_lock)
	exit(lock_fp ? 0 : 1);
    if (lock_fp == 0)
	msg_fatal("open lock file %s: %s",
		  vstring_str(lock_path), vstring_str(why));
    vstream_fprintf(lock_fp, "%*lu\n", (int) sizeof(unsigned long) * 4,
		    (unsigned long) var_pid);
    if (vstream_fflush(lock_fp))
	msg_fatal("cannot update lock file %s: %m", vstring_str(lock_path));
    close_on_exec(vstream_fileno(lock_fp), CLOSE_ON_EXEC);

    /*
     * Lock down the Postfix-writable data directory.
     */
    vstring_sprintf(data_lock_path, "%s/%s.lock", var_data_dir, var_procname);
    set_eugid(var_owner_uid, var_owner_gid);
    data_lock_fp =
	open_lock(vstring_str(data_lock_path), O_RDWR | O_CREAT, 0644, why);
    set_ugid(getuid(), getgid());
    if (data_lock_fp == 0)
	msg_fatal("open lock file %s: %s",
		  vstring_str(data_lock_path), vstring_str(why));
    vstream_fprintf(data_lock_fp, "%*lu\n", (int) sizeof(unsigned long) * 4,
		    (unsigned long) var_pid);
    if (vstream_fflush(data_lock_fp))
	msg_fatal("cannot update lock file %s: %m", vstring_str(data_lock_path));
    close_on_exec(vstream_fileno(data_lock_fp), CLOSE_ON_EXEC);

    /*
     * Clean up.
     */
    vstring_free(why);
    vstring_free(lock_path);
    vstring_free(data_lock_path);

    /*
     * Optionally start the debugger on ourself.
     */
    if (debug_me)
	debug_process();

    /*
     * Finish initialization, last part. We must process configuration files
     * after processing command-line parameters, so that we get consistent
     * results when we SIGHUP the server to reload configuration files.
     */
    master_config();
    master_sigsetup();
    master_flow_init();
    msg_info("daemon started -- version %s, configuration %s",
	     var_mail_version, var_config_dir);

    /*
     * Process events. The event handler will execute the read/write/timer
     * action routines. Whenever something has happened, see if we received
     * any signal in the mean time. Although the master process appears to do
     * multiple things at the same time, it really is all a single thread, so
     * that there are no concurrency conflicts within the master process.
     */
#define MASTER_WATCHDOG_TIME	1000

    watchdog = watchdog_create(MASTER_WATCHDOG_TIME, (WATCHDOG_FN) 0, (char *) 0);
    for (;;) {
#ifdef HAS_VOLATILE_LOCKS
	if (myflock(vstream_fileno(lock_fp), INTERNAL_LOCK,
		    MYFLOCK_OP_EXCLUSIVE) < 0)
	    msg_fatal("refresh exclusive lock: %m");
	if (myflock(vstream_fileno(data_lock_fp), INTERNAL_LOCK,
		    MYFLOCK_OP_EXCLUSIVE) < 0)
	    msg_fatal("refresh exclusive lock: %m");
#endif
	watchdog_start(watchdog);		/* same as trigger servers */
	event_loop(MASTER_WATCHDOG_TIME / 2);
	if (master_gotsighup) {
	    msg_info("reload -- version %s, configuration %s",
		     var_mail_version, var_config_dir);
	    master_gotsighup = 0;		/* this first */
	    master_vars_init();			/* then this */
	    master_refresh();			/* then this */
	}
	if (master_gotsigchld) {
	    if (msg_verbose)
		msg_info("got sigchld");
	    master_gotsigchld = 0;		/* this first */
	    master_reap_child();		/* then this */
	}
    }
}
Esempio n. 8
0
void DecodeDlg::DecodeOutLaunch()
{
	LaunchDialog ld;

	int rmax = m_DecodeOut.GetItemCount();

	if (rmax == 0)
		return;					// Nothing to do

	int scount = m_DecodeOut.GetSelectedCount();	// Read number of selected items

	int *slist = (int *) _alloca(sizeof(int) * (scount + 1));	// Create the selection list
	int *eos = slist + scount;			// End

	m_DecodeOut.GetSelectedList(slist, scount);	// Read the selected items

	int *slp = slist;			// Point into the item list
	int *srchp;

	CFileName here, search, appname;

	CString here_ext, temp;
	CString unsafe(".lnk.exe.com.bat.pif.vbs");

	bool multi;					// Multiple

	int rc, flags;
	short row;

	CFileName tname;			// Will be used for temporary names

	decoder->UD.m_overwrite = TRUE;

	while (scount > 0)
	{
		while (*slp == -1)		// Already handled
			slp++;				// Skip

		here = m_DecodeOut.GetItemText(*slp, 0);	// Read a selected item's text

		here_ext = here.Ext();	// Read the extension
		here_ext.MakeLower();

		if (unsafe.Find(here_ext) >= 0)
		{
			if (ArgMessageBox("Decode & Launch", 
					MB_ICONINFORMATION | MB_OKCANCEL,
					"%s\r\nFor your protection, UUDeview won't\r\nLaunch executable files.",
					(const char *) here) == IDCANCEL)
				break;			// User decided to stop
			else
			{
				slp++;			// Move on to the next item
				scount--;
				continue;
			}
		}

		multi = false;			// Start with multi off

		for (srchp=slp+1; srchp < eos; srchp++)
			if (*srchp != -1)
			{
				search = m_DecodeOut.GetItemText(*srchp, 0);
				if (here_ext.CompareNoCase(search.Ext()) == 0)
				{
					multi = true;	// We've got more than one...
					break;
				}
			}

		row = (short) m_DecodeOut.GetItemData(*slp);	// Read row number

		flags = decoder->UD.GetDFileFlags(row);	// Store old flags

		rc = decoder->UD.DFileTo(row,		// Decode it
			tname.InTemp(m_DecodeOut.GetItemText(*slp, 0), tempFolder));	// To "real" name in temp directory

		decoder->UD.SetDFileFlags(row, flags);	// Restore old flags

		if (rc != CUud32acxCtrl::uudRetOK)
		{
			ArgMessageBox("Decode & Launch", MB_ICONEXCLAMATION | MB_OK,
				"Error decoding:\r\n%s", (const char *) tname);
			break;
		}

		CFileName::XFileAdd(tname);		// Delete it later

		if (options[OPT_LNOPROMPT])
		{
			rc = LDIAG_ALL;			// Always do all

			appname = tname.FindExec();	// Read the app from association
		}
		else
		{
			rc = ld.GetLFN(tname, multi, appname.GetStr(), 
				launchOutList, &launchOutDir, &options[OPT_LNOPROMPT]);

			GetTopLevelParent()->RedrawWindow();

			if (options[OPT_LNOPROMPT])
			{
				rc = LDIAG_ALL;
				OptionButtonSet();
			}
		}

		switch (rc)
		{
			case IDCANCEL:		// Jump out
				goto emex;

			case IDOK:
				multi = false;		// Only handle one
				break;

			case LDIAG_ALL:
				multi = true;		// Handle all
				break;
		}

		if ((rc = tname.Exec(appname)) != 0)		// Always do the first one if we get here
		{
			ArgMessageBox("Launch", MB_ICONEXCLAMATION | MB_OK,
				"Error %d executing:\r\n%s",
				rc, (const char *) tname);
			break;
		}

		m_DecodeOut.SetSel(*slp, FALSE);
		*slp++ = -1;		// This one's been done
		scount--;			// One less selected

		if (!multi)		// One only
			continue;

		for (srchp=slp; srchp < eos; srchp++)
			if (*srchp != -1)
			{
				search = m_DecodeOut.GetItemText(*srchp, 0);
				if (here_ext.CompareNoCase(search.Ext()) == 0)
				{
					if (decoder->UD.DFileTo((short) m_DecodeOut.GetItemData(*srchp), 
						tname.InTemp(m_DecodeOut.GetItemText(*srchp, 0), tempFolder)) != CUud32acxCtrl::uudRetOK)
					{
						ArgMessageBox("Decode & Launch", MB_ICONEXCLAMATION | MB_OK,
							"Error decoding:\r\n%s", (const char *) tname);
						break;
					}

					CFileName::XFileAdd(tname);		// Delete it later

					if ((rc = tname.Exec(appname)) != 0)
					{
						ArgMessageBox("Launch", MB_ICONEXCLAMATION | MB_OK,
							"Error %d executing:\r\n%s",
							rc, (const char *) tname);
						goto emex;
					}
					m_DecodeOut.SetSel(*srchp, FALSE);
					*srchp = -1;		// This one's been done
					scount--;			// One less selected
				}						// Ext match
			}							// Not already done

	}									// scount > 0

emex:

	if (ld.m_hWnd != NULL)
		ld.DestroyWindow();

	GetTopLevelOwner()->RedrawWindow();

}
Esempio n. 9
0
int     main(int argc, char **argv)
{
    char   *script;
    struct stat st;
    char   *slash;
    int     fd;
    int     ch;
    ARGV   *import_env;
    static const CONFIG_STR_TABLE str_table[] = {
	VAR_SENDMAIL_PATH, DEF_SENDMAIL_PATH, &var_sendmail_path, 1, 0,
	VAR_MAILQ_PATH, DEF_MAILQ_PATH, &var_mailq_path, 1, 0,
	VAR_NEWALIAS_PATH, DEF_NEWALIAS_PATH, &var_newalias_path, 1, 0,
	VAR_MANPAGE_DIR, DEF_MANPAGE_DIR, &var_manpage_dir, 1, 0,
	VAR_SAMPLE_DIR, DEF_SAMPLE_DIR, &var_sample_dir, 1, 0,
	VAR_README_DIR, DEF_README_DIR, &var_readme_dir, 1, 0,
	VAR_HTML_DIR, DEF_HTML_DIR, &var_html_dir, 1, 0,
	0,
    };
    int     force_single_instance;
    ARGV   *my_argv;

    /*
     * Fingerprint executables and core dumps.
     */
    MAIL_VERSION_STAMP_ALLOCATE;

    /*
     * Be consistent with file permissions.
     */
    umask(022);

    /*
     * To minimize confusion, make sure that the standard file descriptors
     * are open before opening anything else. XXX Work around for 44BSD where
     * fstat can return EBADF on an open file descriptor.
     */
    for (fd = 0; fd < 3; fd++)
	if (fstat(fd, &st) == -1
	    && (close(fd), open("/dev/null", O_RDWR, 0)) != fd)
	    msg_fatal("open /dev/null: %m");

    /*
     * Set up diagnostics. XXX What if stdin is the system console during
     * boot time? It seems a bad idea to log startup errors to the console.
     * This is UNIX, a system that can run without hand holding.
     */
    if ((slash = strrchr(argv[0], '/')) != 0 && slash[1])
	argv[0] = slash + 1;
    if (isatty(STDERR_FILENO))
	msg_vstream_init(argv[0], VSTREAM_ERR);
    msg_syslog_init(argv[0], LOG_PID, LOG_FACILITY);

    /*
     * Check the Postfix library version as soon as we enable logging.
     */
    MAIL_VERSION_CHECK;

    /*
     * The mail system must be run by the superuser so it can revoke
     * privileges for selected operations. That's right - it takes privileges
     * to toss privileges.
     */
    if (getuid() != 0) {
	msg_error("to submit mail, use the Postfix sendmail command");
	msg_fatal("the postfix command is reserved for the superuser");
    }
    if (unsafe() != 0)
	msg_fatal("the postfix command must not run as a set-uid process");

    /*
     * Parse switches.
     */
    while ((ch = GETOPT(argc, argv, "c:Dv")) > 0) {
	switch (ch) {
	default:
	    msg_fatal("usage: %s [-c config_dir] [-Dv] command", argv[0]);
	case 'c':
	    if (*optarg != '/')
		msg_fatal("-c requires absolute pathname");
	    check_setenv(CONF_ENV_PATH, optarg);
	    break;
	case 'D':
	    check_setenv(CONF_ENV_DEBUG, "");
	    break;
	case 'v':
	    msg_verbose++;
	    check_setenv(CONF_ENV_VERB, "");
	    break;
	}
    }
    force_single_instance = (getenv(CONF_ENV_PATH) != 0);

    /*
     * Copy a bunch of configuration parameters into the environment for easy
     * access by the maintenance shell script.
     */
    mail_conf_read();
    get_mail_conf_str_table(str_table);

    /*
     * Environment import filter, to enforce consistent behavior whether this
     * command is started by hand, or at system boot time. This is necessary
     * because some shell scripts use environment settings to override
     * main.cf settings.
     */
    import_env = argv_split(var_import_environ, ", \t\r\n");
    clean_env(import_env->argv);
    argv_free(import_env);

    check_setenv("PATH", ROOT_PATH);		/* sys_defs.h */
    check_setenv(CONF_ENV_PATH, var_config_dir);/* mail_conf.h */

    check_setenv(VAR_COMMAND_DIR, var_command_dir);	/* main.cf */
    check_setenv(VAR_DAEMON_DIR, var_daemon_dir);	/* main.cf */
    check_setenv(VAR_DATA_DIR, var_data_dir);	/* main.cf */
    check_setenv(VAR_QUEUE_DIR, var_queue_dir);	/* main.cf */
    check_setenv(VAR_CONFIG_DIR, var_config_dir);	/* main.cf */

    /*
     * Do we want to keep adding things here as shell scripts evolve?
     */
    check_setenv(VAR_MAIL_OWNER, var_mail_owner);	/* main.cf */
    check_setenv(VAR_SGID_GROUP, var_sgid_group);	/* main.cf */
    check_setenv(VAR_SENDMAIL_PATH, var_sendmail_path);	/* main.cf */
    check_setenv(VAR_MAILQ_PATH, var_mailq_path);	/* main.cf */
    check_setenv(VAR_NEWALIAS_PATH, var_newalias_path);	/* main.cf */
    check_setenv(VAR_MANPAGE_DIR, var_manpage_dir);	/* main.cf */
    check_setenv(VAR_SAMPLE_DIR, var_sample_dir);	/* main.cf */
    check_setenv(VAR_README_DIR, var_readme_dir);	/* main.cf */
    check_setenv(VAR_HTML_DIR, var_html_dir);	/* main.cf */

    /*
     * Make sure these directories exist. Run the maintenance scripts with as
     * current directory the mail database.
     */
    if (chdir(var_command_dir))
	msg_fatal("chdir(%s): %m", var_command_dir);
    if (chdir(var_daemon_dir))
	msg_fatal("chdir(%s): %m", var_daemon_dir);
    if (chdir(var_queue_dir))
	msg_fatal("chdir(%s): %m", var_queue_dir);

    /*
     * Run the management script.
     */
    if (force_single_instance
	|| argv_split(var_multi_conf_dirs, "\t\r\n, ")->argc == 0) {
	script = concatenate(var_daemon_dir, "/postfix-script", (char *) 0);
	if (optind < 1)
	    msg_panic("bad optind value");
	argv[optind - 1] = script;
	execvp(script, argv + optind - 1);
	msg_fatal("%s: %m", script);
    }

    /*
     * Hand off control to a multi-instance manager.
     */
    else {
	if (*var_multi_wrapper == 0)
	    msg_fatal("multi-instance support is requested, but %s is empty",
		      VAR_MULTI_WRAPPER);
	my_argv = argv_split(var_multi_wrapper, " \t\r\n");
	do {
	    argv_add(my_argv, argv[optind], (char *) 0);
	} while (argv[optind++] != 0);
	execvp(my_argv->argv[0], my_argv->argv);
	msg_fatal("%s: %m", my_argv->argv[0]);
    }
}