ede/edelib2/Run.cpp
2007-03-15 01:06:18 +00:00

237 lines
6.0 KiB
C++

/*
* $Id$
*
* edelib::Run - Library for executing external programs
* Part of Equinox Desktop Environment (EDE).
* Copyright (c) 2000-2006 EDE Authors.
*
* This program is licenced under terms of the
* GNU General Public Licence version 2 or newer.
* See COPYING for details.
*/
#define PREFIX "/usr"
#include "Run.h"
#include "Config.h"
#include <fltk/ask.h>
#include "NLS.h"
#include "process.h"
using namespace fltk;
using namespace edelib;
// GLOBAL NOTE: asprintf() is a GNU extension which is also available under *BSD
// If this is considered a problem, use our tasprintf() instead (in Util.h)
// --------------------------------------------
// Start a process using exec(3)
// This means that there is no handling or control over process,
// it's just forked into background. If you need to chat with
// program or use its output, see edelib::PtyProcess
// --------------------------------------------
int run_fork(const char *cmd, bool wait)
{
int pid, status;
int nulldev;
extern char **environ;
status=0;
if (cmd == NULL)
return (EDERUN_EMPTY);
pid = fork ();
if (pid == -1)
return (EDERUN_FORK_FAILED);
if (pid == 0)
{
char *argv[4];
// child
argv[0] = "sh";
argv[1] = "-c";
argv[2] = (char*)cmd;
argv[3] = NULL;
// The following is to avoid X locking when executing
// terminal based application that requires user input
if ((nulldev = open ("/dev/null", O_RDWR)))
{
close (0); dup (nulldev);
close (1); dup (nulldev);
close (2); dup (nulldev);
}
if (execve ("/bin/sh", argv, environ) == -1)
perror ("/bin/sh");
_exit (EDERUN_EXECVE_FAILED);
}
do
{
if ((wait) && (waitpid (pid, &status, 0) == -1))
{
if (errno != EINTR)
return (EDERUN_WAITPID_FAILED);
}
else {
if (status==127) status=EDERUN_NOT_FOUND;
if (status==126) status=EDERUN_NOT_EXEC;
return status;
}
}
while (1);
return 0;
}
// --------------------------------------------
// Start a process as root user
// We use edelib::PtyProcess to chat with su/sudo. Afterwards
// the program continues undisturbed
// --------------------------------------------
// this is our internal message:
#define CONTMSG "elauncher_ok_to_continue"
// these are part of sudo/su chat:
#define PWDQ "Password:"
#define BADPWD "/bin/su: incorrect password"
#define SUDOBADPWD "Sorry, try again."
int run_as_root(const char *cmd, bool wait)
{
// -- we could check for availibility of sudo, but there's no point
bool use_sudo = false;
Config pGlobalConfig(find_config_file("ede.conf", 0));
pGlobalConfig.set_section("System");
pGlobalConfig.read("UseSudo", use_sudo, false);
// Prepare array as needed by exec()
char *parts[4];
if (use_sudo) {
parts[0] = "/bin/sudo";
parts[1] = "";
// This "continue message" prevents accidentally exposing password
asprintf(&parts[2], "echo %s; %s", CONTMSG, cmd);
parts[3] = NULL;
} else {
parts[0] = "/bin/su";
parts[1] = "-c";
// This "continue message" prevents accidentally exposing password
asprintf(&parts[2], "echo %s; %s", CONTMSG, cmd);
parts[3] = NULL;
}
// the actual command is this:
// cmd_ = strtok(cmd," ");
tryagain:
PtyProcess *child = new PtyProcess();
child->setEnvironment((const char**)environ);
if (child->exec(parts[0], (const char**)parts) < 0) {
return EDERUN_PTY_FAILED;
}
// Wait for process to actually start. Shouldn't last long
while (1) {
int p = child->pid();
if (p != 0 && child->checkPid(p))
break;
int exit = child->checkPidExited(p);
if (exit != -2) {
// Process is DOA
fprintf (stderr, "Edelib: Process has died unexpectedly! Exit status: %d\n",exit);
delete child;
goto tryagain;
}
fprintf (stderr, "Edelib: Process not started yet...\n");
}
// Run program as root using su or sudo
char *line;
const char *pwd = password(_("This program requires administrator privileges.\nPlease enter your password below:"));
if (pwd == 0) return EDERUN_USER_CANCELED;
// Chat routine
while (1) {
line = child->readLine();
// This covers other cases of failed process startup
// Our su command should at least produce CONTMSG
if (line == 0 && child->checkPidExited(child->pid()) != PtyProcess::NotExited) {
// TODO: capture stdout? as in sudo error?
fprintf (stderr, "Edelib: su process has died unexpectedly in chat stage!\n");
delete child;
if (choice_alert (_("Failed to start authentication. Try again"), 0, _("Yes"), _("No")) == 2) return 0;
goto tryagain;
}
if (strncasecmp(line,PWDQ,strlen(PWDQ))== 0)
child->writeLine(pwd,true);
if (strncasecmp(line,CONTMSG,strlen(CONTMSG)) == 0)
break; // program starts...
if ((strncasecmp(line,BADPWD,strlen(BADPWD)) == 0) || (strncasecmp(line,SUDOBADPWD,strlen(SUDOBADPWD)) == 0)) {
// end process
child->waitForChild();
delete child;
if (choice_alert (_("The password is wrong. Try again?"), 0, _("Yes"), _("No")) == 2) return 0;
goto tryagain;
}
}
// Wait for program to end, discarding output
int child_val = child->waitForChild();
if (child_val==127) child_val=EDERUN_NOT_FOUND;
if (child_val==126) child_val=EDERUN_NOT_EXEC;
// deallocate one string we allocated
free(parts[2]);
delete child;
return child_val;
}
static bool done_checks=false;
static bool elauncher_found=false;
// Check availability of various necessary components
// For the moment just elauncher :)
void do_checks()
{
struct stat *buf = (struct stat*)malloc(sizeof(struct stat));
if (stat (PREFIX"/bin/elauncher", buf) == 0)
elauncher_found = true;
else
elauncher_found = false;
}
int edelib::run_program(const char *path, bool wait, bool root, bool use_elauncher)
{
char *execstr;
if (!done_checks) do_checks();
if (use_elauncher && elauncher_found) {
if (root)
asprintf(&execstr,"elauncher --root \"%s\"", path);
else
asprintf(&execstr,"elauncher \"%s\"", path);
run_fork (execstr, false); // elauncher can't wait
} else {
if (root)
return run_as_root(path, wait);
else
return run_fork(path, wait);
}
return 0; // shutup compiler!
}