#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
#include "util.h"
#include "gripes.h"
#include "man.h"
const char *
mkprogname (const char *s) {
const char *t;
t = strrchr (s, '/');
if (t == (char *)NULL)
t = s;
else
t++;
return my_strdup (t);
}
int
is_newer (const char *fa, const char *fb) {
struct stat fa_sb;
struct stat fb_sb;
register int fa_stat;
register int fb_stat;
register int status = 0;
fa_stat = stat (fa, &fa_sb);
if (fa_stat != 0 || fa_sb.st_size == 0)
status = 1;
fb_stat = stat (fb, &fb_sb);
if (fb_stat != 0 || fb_sb.st_size == 0)
status |= 2;
if (status != 0)
return -status;
return (fa_sb.st_mtime > fb_sb.st_mtime);
}
int ruid, rgid, euid, egid, suid;
void
get_permissions (void) {
ruid = getuid();
euid = geteuid();
rgid = getgid();
egid = getegid();
suid = (ruid != euid || rgid != egid);
}
void
no_privileges (void) {
if (suid) {
#if !defined (__CYGWIN32__) && !defined (__BEOS__)
setreuid(ruid, ruid);
setregid(rgid, rgid);
#endif
suid = 0;
}
}
static int interrupted = 0;
static void catch_int(int a) {
interrupted = 1;
}
static int
system1 (const char *command) {
void (*prev_handler)(int) = signal (SIGINT,catch_int);
int ret = system(command);
if (WIFSIGNALED(ret) &&
(WTERMSIG(ret) == SIGINT || WTERMSIG(ret) == SIGQUIT))
exit(1);
if (interrupted)
exit(1);
signal(SIGINT,prev_handler);
return ret;
}
static int
my_system (const char *command) {
int pid, pid2, status, stat;
if (!suid)
return system1 (command);
#ifdef _POSIX_SAVED_IDS
setuid(ruid);
setgid(rgid);
status = system1(command);
setuid(euid);
setgid(egid);
return (WIFEXITED(status) ? WEXITSTATUS(status) : 127);
#endif
fflush(stdout); fflush(stderr);
pid = fork();
if (pid == -1) {
perror(progname);
fatal (CANNOT_FORK, command);
}
if (pid == 0) {
setuid(ruid);
setgid(rgid);
status = system1 (command);
exit(WIFEXITED(status) ? WEXITSTATUS(status) : 127);
}
pid2 = wait (&stat);
if (pid2 == -1) {
perror(progname);
fatal (WAIT_FAILED, command);
}
if (pid2 != pid)
fatal (GOT_WRONG_PID);
if (WIFEXITED(stat) && WEXITSTATUS(stat) != 127)
return WEXITSTATUS(stat);
fatal (CHILD_TERMINATED_ABNORMALLY, command);
return -1;
}
FILE *
my_popen(const char *command, const char *type) {
FILE *r;
if (!suid)
return popen(command, type);
#ifdef _POSIX_SAVED_IDS
setuid(ruid);
setgid(rgid);
r = popen(command, type);
setuid(euid);
setgid(egid);
return r;
#endif
no_privileges();
return popen(command, type);
}
#define NOT_SAFE "/unsafe/"
int
do_system_command (const char *command, int silent) {
int status = 0;
if ((debug & 1) || !strncmp(command, NOT_SAFE, strlen(NOT_SAFE)))
gripe (NO_EXEC, command);
else
status = my_system (command);
if (status && !silent)
gripe (SYSTEM_FAILED, command, status);
return status;
}
char *
my_malloc (int n) {
char *s = malloc(n);
if (!s)
fatal (OUT_OF_MEMORY, n);
return s;
}
char *
my_strdup (const char *s) {
char *t = my_malloc(strlen(s) + 1);
strcpy(t, s);
return t;
}
static int
is_shell_safe(const char *ss, int quoted) {
char *bad = " ;'\\\"<>|";
char *p;
if (quoted)
bad++;
for (p = bad; *p; p++)
if (strchr(ss, *p))
return 0;
return 1;
}
static void
nothing(int x) {}
char *
my_xsprintf (char *format, ...) {
va_list p;
char *s, *ss, *fm;
int len;
len = strlen(format) + 1;
fm = my_strdup(format);
va_start(p, format);
for (s = fm; *s; s++) {
if (*s == '%') {
switch (s[1]) {
case 'Q':
case 'S':
ss = va_arg(p, char *);
if (!is_shell_safe(ss, (s[1] == 'Q')))
return my_strdup(NOT_SAFE);
len += strlen(ss);
s[1] = 's';
break;
case 's':
len += strlen(va_arg(p, char *));
break;
case 'd':
case 'o':
case 'c':
len += 20;
nothing(va_arg(p, int));
break;
default:
fprintf(stderr,
"my_xsprintf called with %s\n",
format);
exit(1);
}
}
}
va_end(p);
s = my_malloc(len);
va_start(p, format);
vsprintf(s, fm, p);
va_end(p);
return s;
}