// // aegis - project change supervisor // Copyright (C) 2002-2008 Peter Miller // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see // . // #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define MAX_CMD_RPT 36 static void who_and_where(int uid, int gid, const nstring &dir) { trace(("mark\n")); if (!option_verbose_get()) return; static int last_uid; static int last_gid; static nstring last_dir; if (last_dir.empty()) { os_become_orig_query(&last_uid, &last_gid, (int *)0); last_dir = nstring(os_curdir()); } nstring rdir = os_pathname(dir, true); if (last_dir != rdir) { sub_context_ty sc; last_dir = rdir; sc.var_set_string("File_Name", dir); sc.error_intl(i18n("cd $filename")); } if (last_uid != uid || last_gid != gid) { sub_context_ty sc; last_uid = uid; struct passwd *pw = getpwuid_cached(uid); if (pw) sc.var_set_format("Name1", "\"%s\"", pw->pw_name); else sc.var_set_long("Name1", uid); last_gid = gid; struct group *gr = getgrgid_cached(gid); if (gr) sc.var_set_format("Name2", "\"%s\"", gr->gr_name); else sc.var_set_long("Name2", gid); sc.error_intl(i18n("user $name1, group $name2")); } } int os_execute_retcode(string_ty *cmd, int flags, string_ty *dir) { trace(("mark\n")); return os_execute_retcode(nstring(cmd), flags, nstring(dir)); } int os_execute_retcode(const nstring &cmd, int flags, const nstring &dir) { trace(("os_execute_retcode()\n{\n")); int result = 0; os_become_must_be_active(); int uid = 0, gid = 0, um = 0; os_become_query(&uid, &gid, &um); who_and_where(uid, gid, dir); nstring cmd2 = cmd; if (!(flags & OS_EXEC_FLAG_SILENT) && cmd.size() > MAX_CMD_RPT) cmd2 = nstring::format("%.*s...", MAX_CMD_RPT - 3, cmd.c_str()); // // Remember the user name, so we can set the environment variable. // We do this before the fork, so it will be cached for next time. // (void)getpwuid_cached(uid); // // fork to get a process to do the command // trace(("mark\n")); int child = fork(); switch (child) { case -1: nfatal("fork"); default: { // // The parent process waits // RETSIGTYPE (*hold)(int) = signal(SIGINT, SIG_IGN); if (hold != SIG_IGN) signal(SIGINT, os_interrupt); trace(("parent\n")); fflush(stderr); result = os_waitpid_status(child, cmd2.c_str()); trace(("result = 0x%04X\n", result)); } break; case 0: // // become the user for real // trace(("child\n")); while (os_become_active()) os_become_undo(); undo_cancel(); os_setgid(gid); os_setuid(uid); umask(um); // // change directory to the appropriate directory. // if (dir) os_chdir(dir); // // We are about to break stdin, one of the file descriptors // used by programs to determine the terminal size. Set the // appropriate environment variables to that the sub-program // agrees with Aegis. // trace(("child\n")); env_set_page(); // // Set the user name to match the UID // (we cached it before the fork) // { struct passwd *pw = getpwuid_cached(uid); if (pw) { env_set("USER", pw->pw_name); env_set("LOGIN", pw->pw_name); env_set("USERNAME", pw->pw_name); env_set("LOGNAME", pw->pw_name); env_set("HOME", pw->pw_dir); } } // // Redirect stdin from a broken pipe. // (Don't redirect stdin if not logging, for manual tests.) // if (!(flags & OS_EXEC_FLAG_INPUT)) { int pfd[2]; if (pipe(pfd)) nfatal("pipe"); if (close(0)) nfatal("close stdin"); int n = dup(pfd[0]); if (n < 0) nfatal("dup"); if (n != 0) fatal_raw("dup gave %d, not 0 (bug)", n); if (close(pfd[0]) || close(pfd[1])) nfatal("close pipe ends"); } // // let the log file (user) know what we did // trace(("child\n")); if (!(flags & OS_EXEC_FLAG_SILENT)) { sub_context_ty sc; sc.var_set_string("MeSsaGe", cmd); sc.error_intl("$message"); } // // invoke the command through sh(1) // trace(("child\n")); const char *shell = os_shell(); execl(shell, shell, "-ec", cmd.c_str(), (char *)0); int errno_old = errno; sub_context_ty sc; sc.errno_setx(errno_old); sc.var_set_charstar("File_Name", shell); sc.fatal_intl(i18n("exec \"$filename\": $errno")); // NOTREACHED } trace(("parent\n")); if (result && (flags & OS_EXEC_FLAG_ERROK)) { sub_context_ty sc; sc.var_set_string("Command", cmd); sc.var_set_long("Number", result); sc.error_intl ( i18n("warning: command \"$command\" exit status $number") ); result = 0; } trace(("return %d;\n", result)); trace(("}\n")); return result; }