mirror of
https://github.com/edeproject/ede.git
synced 2023-08-10 21:13:03 +03:00
238 lines
6.0 KiB
C++
238 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;
|
||
|
|
||
|
// TODO: fix password dialog so that Cancel can be detected
|
||
|
// At the moment it's impossible to tell if the password is blank
|
||
|
const char *pwd = password(_("This program requires administrator privileges.\nPlease enter your password below:"));
|
||
|
|
||
|
// 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!
|
||
|
}
|