femtolisp/llt/dirpath.c

233 lines
5.3 KiB
C
Raw Normal View History

2008-06-30 21:53:51 -04:00
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <assert.h>
#include <errno.h>
#include <limits.h>
#include <sys/stat.h>
#include <sys/types.h>
#include "dtypes.h"
2008-06-30 21:53:51 -04:00
#ifdef WIN32
#include <malloc.h>
#include <sys/timeb.h>
#include <windows.h>
#undef NO_ERROR
#undef MOD_SHIFT
#undef TRUE
#undef FALSE
#undef VOID
#else
#include <sys/time.h>
#include <sys/poll.h>
#include <unistd.h>
#endif
#include "dirpath.h"
2008-06-30 21:53:51 -04:00
void get_cwd(char *buf, size_t size)
{
#ifndef WIN32
getcwd(buf, size);
#else
GetCurrentDirectory(size, buf);
#endif
}
int set_cwd(char *buf)
{
#ifndef WIN32
if (chdir(buf) == -1)
return 1;
#else
if (SetCurrentDirectory(buf) == 0)
return 1;
#endif
return 0;
}
// destructively convert path to directory part
void path_to_dirname(char *path)
{
char *sep = strrchr(path, PATHSEP);
if (sep != NULL) {
*sep = '\0';
}
else {
path[0] = '\0';
}
}
2008-06-30 21:53:51 -04:00
#ifdef LINUX
char *get_exename(char *buf, size_t size)
{
char linkname[64]; /* /proc/<pid>/exe */
pid_t pid;
ssize_t ret;
/* Get our PID and build the name of the link in /proc */
pid = getpid();
if (snprintf(linkname, sizeof(linkname), "/proc/%i/exe", pid) < 0)
return NULL;
/* Now read the symbolic link */
ret = readlink(linkname, buf, size);
/* In case of an error, leave the handling up to the caller */
if (ret == -1)
return NULL;
/* Report insufficient buffer size */
if ((size_t)ret >= size)
return NULL;
/* Ensure proper NUL termination */
buf[ret] = 0;
return buf;
}
2013-06-03 21:40:14 -04:00
#elif defined(OPENBSD)
get_exename() for OpenBSD. (#17) get_exename() for OpenBSD. Issue `get_exename()` gets the pathname of the current process. In femtolisp, this is used to set the top-level `*install-dir*` which in turn is used as the location of the system image, `flisp.boot`. There is only a trivial implementation of `get_exename()` for OpenBSD that simply returns `NULL`. A minor consequence is that the unit test will fail for the default build (make) because the system image cannot be found. Fix This commit provides an implementation of `get_exename()` for OpenBSD, so that the system image can be found. Unlike, say, Linux or FreeBSD, OpenBSD doesn't have a system call to get the path of the current (or any, for that matter) process. The present code contains some logic that was put together to emulate the behavior of the Linux and FreeBSD variants of `get_exename()` as best as possible. It works as described by the following. (1) Call `sysctl(3)` (with `CTL_KERN` -> `KERN_PROC_ARGS` -> `KERN_PROC_ARGV`) to get the "`argv[0]`" of the current process. If the program (flisp) was called in "`basename` form" (i.e. as "flisp"), then go to (2). Otherwise, return the value from `sysctl(3)` which will be an absolute or relative pathname, e.g. "/usr/local/bin/flisp" or "../flisp". The code for (1) was adapted from old OpenBSD-specific `tmux` code that has since been abandoned by the author only because he deemed it "too expensive". For that code, see http://sourceforge.net/p/tmux/tmux-code/ci/8c259f562be24570a19fd94b223034ae8c6e4277/tree/osdep-openbsd.c (2) Since we now only have "flisp", we need to find out where it is located on the system. We assume that a program like the shell had to crawl `PATH` to find "flisp"; hence we do the same. The code for (2) was adapted from the `which` utility in OpenBSD. See http://cvsweb.openbsd.org/cgi-bin/cvsweb/~checkout~/src/usr.bin/which/which.c?rev=1.20&content-type=text/plain Finally, any error condition returns `NULL`, which is the same behavior of the other `get_exename()` variants. * Resolve relative pathnames to absolute, for OpenBSD get_exename().
2016-11-15 15:12:14 -05:00
#include <sys/param.h>
#include <sys/sysctl.h>
2013-06-03 21:40:14 -04:00
char *get_exename(char *buf, size_t size)
{
get_exename() for OpenBSD. (#17) get_exename() for OpenBSD. Issue `get_exename()` gets the pathname of the current process. In femtolisp, this is used to set the top-level `*install-dir*` which in turn is used as the location of the system image, `flisp.boot`. There is only a trivial implementation of `get_exename()` for OpenBSD that simply returns `NULL`. A minor consequence is that the unit test will fail for the default build (make) because the system image cannot be found. Fix This commit provides an implementation of `get_exename()` for OpenBSD, so that the system image can be found. Unlike, say, Linux or FreeBSD, OpenBSD doesn't have a system call to get the path of the current (or any, for that matter) process. The present code contains some logic that was put together to emulate the behavior of the Linux and FreeBSD variants of `get_exename()` as best as possible. It works as described by the following. (1) Call `sysctl(3)` (with `CTL_KERN` -> `KERN_PROC_ARGS` -> `KERN_PROC_ARGV`) to get the "`argv[0]`" of the current process. If the program (flisp) was called in "`basename` form" (i.e. as "flisp"), then go to (2). Otherwise, return the value from `sysctl(3)` which will be an absolute or relative pathname, e.g. "/usr/local/bin/flisp" or "../flisp". The code for (1) was adapted from old OpenBSD-specific `tmux` code that has since been abandoned by the author only because he deemed it "too expensive". For that code, see http://sourceforge.net/p/tmux/tmux-code/ci/8c259f562be24570a19fd94b223034ae8c6e4277/tree/osdep-openbsd.c (2) Since we now only have "flisp", we need to find out where it is located on the system. We assume that a program like the shell had to crawl `PATH` to find "flisp"; hence we do the same. The code for (2) was adapted from the `which` utility in OpenBSD. See http://cvsweb.openbsd.org/cgi-bin/cvsweb/~checkout~/src/usr.bin/which/which.c?rev=1.20&content-type=text/plain Finally, any error condition returns `NULL`, which is the same behavior of the other `get_exename()` variants. * Resolve relative pathnames to absolute, for OpenBSD get_exename().
2016-11-15 15:12:14 -05:00
int mib[4];
pid_t pid;
size_t len, plen;
char **argv, **argv2;
char *p, *path, *pathcpy, filename[PATH_MAX];
struct stat sbuf;
pid = getpid();
mib[0] = CTL_KERN;
mib[1] = KERN_PROC_ARGS;
mib[2] = pid;
mib[3] = KERN_PROC_ARGV;
buf = NULL;
argv = NULL;
len = 128;
// Now, play The Guessing Game with sysctl(3) as to how big argv
// is supposed to be. (It's loads of fun for the whole family!)
while (len < SIZE_MAX / 2) {
len *= 2;
if ((argv2 = realloc(argv, len)) == NULL)
break;
argv = argv2;
if (sysctl(mib, 4, argv, &len, NULL, 0) == -1) {
if (errno == ENOMEM)
continue; // Go back and realloc more memory.
break; // Bail for some other error in sysctl(3).
}
// If you made it here, congrats! You guessed right!
if (*argv != NULL)
buf = strdup(*argv);
break;
}
free(argv);
// If no error occurred in the sysctl(3) KERN_PROC_ARGV call
// above, then buf at this point contains some kind of pathname.
if (buf != NULL) {
if (strchr(buf, '/') == NULL) {
// buf contains a `basename`-style pathname (i.e. "foo",
// as opposed to "../foo" or "/usr/bin/foo"); search the
// PATH for its location. (BTW the setgid(2), setuid(2)
// calls are a pre-condition for the access(2) call
// later.)
if ( (path = getenv("PATH")) != NULL &&
!setgid(getegid()) && !setuid(geteuid()) ) {
// The strdup(3) call below, if successful, will
// allocate memory for the PATH string returned by
// getenv(3) above. This is necessary because the man
// page of getenv(3) says that its return value
// "should be considered read-only"; however, the
// strsep(3) call below is going to be destructively
// modifying that value. ("Hulk smash!")
if ((path = strdup(path)) != NULL) {
pathcpy = path;
len = strlen(buf);
while ((p = strsep(&pathcpy, ":")) != NULL) {
if (*p == '\0') p = ".";
plen = strlen(p);
// strip trailing '/'
while (p[plen-1] == '/') p[--plen] = '\0';
if (plen + 1 + len < sizeof(filename)) {
snprintf(filename, sizeof(filename), "%s/%s", p, buf);
if ( (stat(filename, &sbuf) == 0) &&
S_ISREG(sbuf.st_mode) &&
access(filename, X_OK) == 0 ) {
buf = strdup(filename);
break;
}
}
}
free(path); // free the strdup(3) memory allocation.
}
}
else buf = NULL; // call to getenv(3) or [sg]ete?[ug]id(2) failed.
}
if ( buf != NULL && *buf != '/' ) {
// buf contains a relative pathname (e.g. "../foo");
// resolve this to an absolute pathname.
if ( strlcpy(filename, buf, sizeof(filename)) >= sizeof(filename) ||
realpath(filename, buf) == NULL )
buf = NULL;
}
}
return buf;
2013-06-03 21:40:14 -04:00
}
#elif defined(FREEBSD) || defined(NETBSD)
2012-02-26 23:00:47 -05:00
#include <sys/types.h>
#include <sys/sysctl.h>
char *get_exename(char *buf, size_t size)
{
int mib[4];
mib[0] = CTL_KERN;
#if defined(FREEBSD)
2012-02-26 23:00:47 -05:00
mib[1] = KERN_PROC;
mib[2] = KERN_PROC_PATHNAME;
mib[3] = -1;
#else
mib[1] = KERN_PROC_ARGS;
mib[2] = -1;
mib[3] = KERN_PROC_PATHNAME;
#endif
2012-02-26 23:00:47 -05:00
sysctl(mib, 4, buf, &size, NULL, 0);
return buf;
}
2008-06-30 21:53:51 -04:00
#elif defined(WIN32)
char *get_exename(char *buf, size_t size)
{
if (GetModuleFileName(NULL, buf, size) == 0)
return NULL;
return buf;
}
#elif defined(MACOSX)
2015-05-06 18:36:41 -04:00
#include <mach-o/dyld.h>
2008-06-30 21:53:51 -04:00
char *get_exename(char *buf, size_t size)
{
2015-05-06 18:36:41 -04:00
uint32_t bufsize = (uint32_t)size;
if (_NSGetExecutablePath(buf, &bufsize))
return NULL;
2008-06-30 21:53:51 -04:00
return buf;
}
#endif