exec change
- From: Havoc Pennington <hp redhat com>
- To: gtk-devel-list gnome org
- Subject: exec change
- Date: 07 Sep 2000 20:08:18 -0400
Hi,
Owen didn't like that searching the path kept you from passing in an
environment (because there's no execvpe()), so I cut-and-pasted
and slightly modified the execvp() implementation from GNU libc
adding the ability to pass in an environment.
I was going to do it with g_find_in_path(), so I implemented that too,
in gutils.c, and this uses g_file_test() in a new file gfileutils.c.
g_find_program_in_path() is separate from g_find_in_path() because
searching PATH requires some special steps, g_find_in_path
(g_getenv(PATH), program) is not correct. Anyway, I didn't end up
using these in gexec.c but I guess they're useful still.
Havoc
gutils.c:
gchar*
g_find_in_path (const gchar *path_list,
const gchar *filename)
{
gchar **paths;
gchar **p;
gchar *f;
paths = g_strsplit (path_list, ":", -1);
p = paths;
while (*p)
{
f = g_strconcat (*p, "/", filename, NULL);
if (g_file_test (f, G_FILE_TEST_IS_EXECUTABLE))
{
g_strfreev (paths);
return f;
}
g_free (f);
++p;
}
g_strfreev (paths);
return NULL;
}
/* Based on execvp() from GNU Libc.
* Some of this code is cut-and-pasted into gexec.c
*/
static gchar*
my_strchrnul (const gchar *str, gchar c)
{
gchar *p = (gchar*)str;
while (*p && (*p != c))
++p;
return p;
}
gchar*
g_find_program_in_path (const gchar *file)
{
gchar *path, *p, *name, *freeme;
size_t len;
size_t pathlen;
path = g_getenv ("PATH");
if (path == NULL)
{
/* There is no `PATH' in the environment. The default
* search path in libc is the current directory followed by
* the path `confstr' returns for `_CS_PATH'.
*/
/* In GLib we put . last, for security, and don't use the
* unportable confstr(); UNIX98 does not actually specify
* what to search if PATH is unset. POSIX may, dunno.
*/
path = "/bin:/usr/bin:.";
}
len = strlen (file) + 1;
pathlen = strlen (path);
freeme = name = g_malloc (pathlen + len + 1);
/* Copy the file name at the top, including '\0' */
memcpy (name + pathlen + 1, file, len);
name = name + pathlen;
/* And add the slash before the filename */
*name = '/';
p = path;
do
{
char *startp;
path = p;
p = my_strchrnul (path, ':');
if (p == path)
/* Two adjacent colons, or a colon at the beginning or the end
* of `PATH' means to search the current directory.
*/
startp = name + 1;
else
startp = memcpy (name - (p - path), path, p - path);
if (g_file_test (startp, G_FILE_TEST_IS_EXECUTABLE))
{
gchar *ret;
ret = g_strdup (startp);
g_free (freeme);
return ret;
}
}
while (*p++ != '\0');
g_free (freeme);
return NULL;
}
gfileutils.c:
gboolean
g_file_test (const gchar *filename,
GFileTest test)
{
struct stat s;
if (test & G_FILE_TEST_EXISTS)
return (access (filename, F_OK) == 0);
else if (test & G_FILE_TEST_IS_EXECUTABLE)
return (access (filename, X_OK) == 0);
else
{
if (stat (filename, &s) < 0)
return FALSE;
if ((test & G_FILE_TEST_IS_DIR) &&
S_ISDIR (s.st_mode))
return TRUE;
else if ((test & G_FILE_TEST_IS_SYMLINK) &&
S_ISLNK (s.st_mode))
return TRUE;
else
return FALSE;
}
}
gexec.c:
/* Based on execvp from GNU C Library */
static void
script_execute (const gchar *file,
gchar **argv,
gchar **envp,
gboolean search_path)
{
/* Count the arguments. */
int argc = 0;
while (argv[argc])
++argc;
/* Construct an argument list for the shell. */
{
gchar **new_argv;
new_argv = g_new0 (gchar*, argc + 1);
new_argv[0] = (char *) "/bin/sh";
new_argv[1] = (char *) file;
while (argc > 1)
{
new_argv[argc] = argv[argc - 1];
--argc;
}
/* Execute the shell. */
if (envp)
execve (new_argv[0], new_argv, envp);
else
execv (new_argv[0], new_argv);
g_free (new_argv);
}
}
static gchar*
my_strchrnul (const gchar *str, gchar c)
{
gchar *p = (gchar*) str;
while (*p && (*p != c))
++p;
return p;
}
static gint
g_execute (const gchar *file,
gchar **argv,
gchar **envp,
gboolean search_path)
{
if (*file == '\0')
{
/* We check the simple case first. */
errno = ENOENT;
return -1;
}
if (!search_path || strchr (file, '/') != NULL)
{
/* Don't search when it contains a slash. */
if (envp)
execve (file, argv, envp);
else
execv (file, argv);
if (errno == ENOEXEC)
script_execute (file, argv, envp, FALSE);
}
else
{
gboolean got_eacces = 0;
char *path, *p, *name, *freeme;
size_t len;
size_t pathlen;
path = g_getenv ("PATH");
if (path == NULL)
{
/* There is no `PATH' in the environment. The default
* search path in libc is the current directory followed by
* the path `confstr' returns for `_CS_PATH'.
*/
/* In GLib we put . last, for security, and don't use the
* unportable confstr(); UNIX98 does not actually specify
* what to search if PATH is unset. POSIX may, dunno.
*/
path = "/bin:/usr/bin:.";
}
len = strlen (file) + 1;
pathlen = strlen (path);
freeme = name = g_malloc (pathlen + len + 1);
/* Copy the file name at the top, including '\0' */
memcpy (name + pathlen + 1, file, len);
name = name + pathlen;
/* And add the slash before the filename */
*name = '/';
p = path;
do
{
char *startp;
path = p;
p = my_strchrnul (path, ':');
if (p == path)
/* Two adjacent colons, or a colon at the beginning or the end
* of `PATH' means to search the current directory.
*/
startp = name + 1;
else
startp = memcpy (name - (p - path), path, p - path);
/* Try to execute this name. If it works, execv will not return. */
if (envp)
execve (startp, argv, envp);
else
execv (startp, argv);
if (errno == ENOEXEC)
script_execute (startp, argv, envp, search_path);
switch (errno)
{
case EACCES:
/* Record the we got a `Permission denied' error. If we end
* up finding no executable we can use, we want to diagnose
* that we did find one but were denied access.
*/
got_eacces = TRUE;
/* FALL THRU */
case ENOENT:
#ifdef ESTALE
case ESTALE:
#endif
#ifdef ENOTDIR
case ENOTDIR:
#endif
/* Those errors indicate the file is missing or not executable
* by us, in which case we want to just try the next path
* directory.
*/
break;
default:
/* Some other error means we found an executable file, but
* something went wrong executing it; return the error to our
* caller.
*/
g_free (freeme);
return -1;
}
}
while (*p++ != '\0');
/* We tried every element and none of them worked. */
if (got_eacces)
/* At least one failure was due to permissions, so report that
* error.
*/
errno = EACCES;
g_free (freeme);
}
/* Return the error from the last attempt (probably ENOENT). */
return -1;
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]