/*
* Copyright (c) 1997 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#ifdef WIN32
# include <io.h>
#else
# include <fcntl.h>
#endif
#include <string.h>
#include <stdio.h>
#include "env.c"
#if defined __linux__ || defined __FreeBSD__
/* Look at /proc/$$/{exe,file} for the current executable
Returns malloc()ed string. Caller must free.
Returns NULL if can't be found.
Note that FreeBSD has /proc unmounted by default. You'd think we could
get this info via the kvm interface, but it turns out that to get
kvm_getprocs()/kvm_read() to return any information we don't already
have, we need read-access to /boot/kmem, which we don't have. And I
couldn't get to work anyway. Email me (philip-at-pied.nu) if want a
stab at the code. */
char *par_current_exec_proc( void )
{
char proc_path[MAXPATHLEN + 1], link[MAXPATHLEN + 1];
char *ret = NULL;
int n;
n = sprintf( proc_path, "/proc/%i/%s", (int)getpid(),
#if defined __FreeBSD__
"file"
#else
"exe"
#endif
);
if( n < 0 )
return NULL;
n = readlink( proc_path, link, MAXPATHLEN);
if( n < 0 )
return NULL;
ret = (char *)malloc( n+1 );
if( ret == NULL )
return NULL;
memcpy( ret, link, n );
ret[n] = '\0';
return ret;
}
#endif
char *par_current_exec( void )
{
#if defined __linux__ || defined __FreeBSD__
return par_current_exec_proc();
#else
return NULL;
#endif
}
char *par_findprog(char *prog, const char *path) {
char *p, *endp, filename[MAXPATHLEN];
char *par_temp = par_getenv("PAR_TEMP");
/* NOTE: This code is #include'd both from a plain C program (boot.c)
* and our custom Perl interpreter (main.c). In the latter case,
* lstat() or stat() may be #define'd as calls into PerlIO and
* expect pointer to a Stat_t as second parameter, rather than a pointer
* to a struct stat. Try to distinguish these cases by checking
* whether Stat_t is defined. */
#ifndef Stat_t
#define Stat_t struct stat
#endif
Stat_t statbuf;
#ifdef WIN32
if ( GetModuleFileName(0, filename, MAXPATHLEN) ) {
par_setenv("PAR_PROGNAME", filename);
return strdup(filename);
}
#endif
/* Special case if prog contains '/' */
if (strstr(prog, dir_sep)) {
par_setenv("PAR_PROGNAME", prog);
return prog;
}
/* Walk through PATH (path), looking for ourself (prog).
This fails if we are invoked in an obscure manner;
Basically, execvp( "/full/path/to/prog", "prog", NULL ) and
"/full/path/to" isn't in $PATH. Of course, I can't think
of a situation this will happen. */
/* Note: use a copy of path as strtok() modifies its first argument */
for (p = strtok(strdup(path), path_sep); p != NULL; p = strtok(NULL, path_sep)) {
/* an empty PATH element means the current directory */
if (*p == '\0') p = ".";
if ( par_temp != NULL && ( strcmp(par_temp, p) == 0 ) ) {
continue;
}
/* strip trailing '/' */
endp = p + strlen(p) - 1;
while (p < endp && *endp == *dir_sep) {
*endp ='\0';
endp--;
}
if (strlen(p) + strlen(dir_sep) + strlen(prog) + 1 < sizeof(filename)) {
sprintf(filename, "%s%s%s", p, dir_sep, prog);
if (stat(filename, &statbuf) == 0
&& S_ISREG(statbuf.st_mode)
&& access(filename, X_OK) == 0) {
par_setenv("PAR_PROGNAME", filename);
return strdup(filename);
}
}
}
par_setenv("PAR_PROGNAME", prog);
return prog;
}
char *par_basename (const char *name) {
char *base = strrchr(name, *dir_sep);
return strdup(base != NULL ? base + 1 : name);
}
char *par_dirname (const char *path) {
char dname[MAXPATHLEN];
char *endp;
/* Empty or NULL string gets treated as "." */
if (path == NULL || *path == '\0') {
return strdup(".");
}
if (strlen(path) + 1 > sizeof(dname))
return NULL;
strcpy(dname, path);
/* Strip trailing slashes */
endp = dname + strlen(dname) - 1;
while (endp > dname && *endp == *dir_sep) {
*endp = '\0';
endp--;
}
endp = strrchr(dname, *dir_sep);
if (endp == NULL)
return strdup(".");
if (endp > dname)
*endp = '\0';
return strdup(dname);
}
void par_init_env () {
char *buf;
/* ignore PERL5LIB et al. as they make no sense for a self-contained executable */
par_unsetenv("PERL5LIB");
par_unsetenv("PERLLIB");
par_unsetenv("PERL5OPT");
par_unsetenv("PERLIO");
par_unsetenv("PAR_INITIALIZED");
par_unsetenv("PAR_SPAWNED");
par_unsetenv("PAR_TEMP");
par_unsetenv("PAR_CLEAN");
par_unsetenv("PAR_DEBUG");
par_unsetenv("PAR_CACHE");
par_unsetenv("PAR_PROGNAME");
if ( (buf = par_getenv("PAR_GLOBAL_DEBUG")) != NULL ) {
par_setenv("PAR_DEBUG", buf);
}
if ( (buf = par_getenv("PAR_GLOBAL_TMPDIR")) != NULL ) {
par_setenv("PAR_TMPDIR", buf);
}
if ( (buf = par_getenv("PAR_GLOBAL_TEMP")) != NULL ) {
par_setenv("PAR_TEMP", buf);
}
else if ( (buf = par_getenv("PAR_GLOBAL_CLEAN")) != NULL ) {
par_setenv("PAR_CLEAN", buf);
}
par_setenv("PAR_INITIALIZED", "1");
return;
}
int par_env_clean () {
char *val = par_getenv("PAR_CLEAN");
if (val == NULL || *val == '\0' || *val == '0')
return 0;
return 1;
}
void par_die(const char *format, ...)
{
va_list ap;
va_start(ap, format);
vfprintf(stderr, format, ap);
va_end(ap);
exit(255);
}