1/* 2 * util.c 3 * 4 * Copyright (c) 1990, 1991, John W. Eaton. 5 * 6 * You may distribute under the terms of the GNU General Public 7 * License as specified in the file COPYING that comes with the man 8 * distribution. 9 * 10 * John W. Eaton 11 * jwe@che.utexas.edu 12 * Department of Chemical Engineering 13 * The University of Texas at Austin 14 * Austin, Texas 78712 15 */ 16 17#include <stdio.h> 18#include <string.h> 19#include <stdlib.h> 20#include <stdarg.h> 21#include <signal.h> 22#include <sys/types.h> 23#include <sys/stat.h> 24#include <sys/wait.h> 25#include <unistd.h> 26 27#include "util.h" 28#include "gripes.h" 29#include "man.h" /* for debug */ 30 31/* 32 * Extract last element of a name like /foo/bar/baz. 33 */ 34const char * 35mkprogname (const char *s) { 36 const char *t; 37 38 t = strrchr (s, '/'); 39 if (t == (char *)NULL) 40 t = s; 41 else 42 t++; 43 44 return my_strdup (t); 45} 46 47/* 48 * Is file a nonempty and newer than file b? 49 * 50 * case: 51 * 52 * a newer than b returns 1 53 * a older than b returns 0 54 * stat on a fails or a empty returns -1 55 * stat on b fails or b empty returns -2 56 * both fail or empty returns -3 57 */ 58int 59is_newer (const char *fa, const char *fb) { 60 struct stat fa_sb; 61 struct stat fb_sb; 62 register int fa_stat; 63 register int fb_stat; 64 register int status = 0; 65 66 fa_stat = stat (fa, &fa_sb); 67 if (fa_stat != 0 || fa_sb.st_size == 0) 68 status = 1; 69 70 fb_stat = stat (fb, &fb_sb); 71 if (fb_stat != 0 || fb_sb.st_size == 0) 72 status |= 2; 73 74 if (status != 0) 75 return -status; 76 77 return (fa_sb.st_mtime > fb_sb.st_mtime); 78} 79 80int ruid, rgid, euid, egid, suid; 81 82void 83get_permissions (void) { 84 ruid = getuid(); 85 euid = geteuid(); 86 rgid = getgid(); 87 egid = getegid(); 88 suid = (ruid != euid || rgid != egid); 89} 90 91void 92no_privileges (void) { 93 if (suid) { 94#if !defined (__CYGWIN32__) && !defined (__BEOS__) 95 setreuid(ruid, ruid); 96 setregid(rgid, rgid); 97#endif 98 suid = 0; 99 } 100} 101 102/* 103 * What to do upon an interrupt? Experience shows that 104 * if we exit immediately, sh notices that its child has 105 * died and will try to fiddle with the tty. 106 * Simultaneously, also less will fiddle with the tty, 107 * resetting the mode before exiting. 108 * This leads to undesirable races. So, we catch SIGINT here 109 * and exit after the child has exited. 110 */ 111static int interrupted = 0; 112static void catch_int(int a) { 113 interrupted = 1; 114} 115 116static int 117system1 (const char *command) { 118 void (*prev_handler)(int) = signal (SIGINT,catch_int); 119 int ret = system(command); 120 121 /* child terminated with signal? */ 122 if (WIFSIGNALED(ret) && 123 (WTERMSIG(ret) == SIGINT || WTERMSIG(ret) == SIGQUIT)) 124 exit(1); 125 126 /* or we caught an interrupt? */ 127 if (interrupted) 128 exit(1); 129 130 signal(SIGINT,prev_handler); 131 return ret; 132} 133 134static int 135my_system (const char *command) { 136 int pid, pid2, status, stat; 137 138 if (!suid) 139 return system1 (command); 140 141#ifdef _POSIX_SAVED_IDS 142 143 /* we need not fork */ 144 setuid(ruid); 145 setgid(rgid); 146 status = system1(command); 147 setuid(euid); 148 setgid(egid); 149 return (WIFEXITED(status) ? WEXITSTATUS(status) : 127); 150#endif 151 152 fflush(stdout); fflush(stderr); 153 pid = fork(); 154 if (pid == -1) { 155 perror(progname); 156 fatal (CANNOT_FORK, command); 157 } 158 if (pid == 0) { 159 setuid(ruid); 160 setgid(rgid); 161 status = system1 (command); 162 exit(WIFEXITED(status) ? WEXITSTATUS(status) : 127); 163 } 164 pid2 = wait (&stat); 165 if (pid2 == -1) { 166 perror(progname); 167 fatal (WAIT_FAILED, command); /* interrupted? */ 168 } 169 if (pid2 != pid) 170 fatal (GOT_WRONG_PID); 171 if (WIFEXITED(stat) && WEXITSTATUS(stat) != 127) 172 return WEXITSTATUS(stat); 173 fatal (CHILD_TERMINATED_ABNORMALLY, command); 174 return -1; /* not reached */ 175} 176 177FILE * 178my_popen(const char *command, const char *type) { 179 FILE *r; 180 181 if (!suid) 182 return popen(command, type); 183 184#ifdef _POSIX_SAVED_IDS 185 setuid(ruid); 186 setgid(rgid); 187 r = popen(command, type); 188 setuid(euid); 189 setgid(egid); 190 return r; 191#endif 192 193 no_privileges(); 194 return popen(command, type); 195} 196 197#define NOT_SAFE "/unsafe/" 198 199/* 200 * Attempt a system () call. 201 */ 202int 203do_system_command (const char *command, int silent) { 204 int status = 0; 205 206 /* 207 * If we're debugging, don't really execute the command 208 */ 209 if ((debug & 1) || !strncmp(command, NOT_SAFE, strlen(NOT_SAFE))) 210 gripe (NO_EXEC, command); 211 else 212 status = my_system (command); 213 214 if (status && !silent) 215 gripe (SYSTEM_FAILED, command, status); 216 217 return status; 218} 219 220char * 221my_malloc (int n) { 222 char *s = malloc(n); 223 if (!s) 224 fatal (OUT_OF_MEMORY, n); 225 return s; 226} 227 228char * 229my_strdup (const char *s) { 230 char *t = my_malloc(strlen(s) + 1); 231 strcpy(t, s); 232 return t; 233} 234 235/* 236 * Call: my_xsprintf(format,s1,s2,...) where format only contains %s/%S/%Q 237 * (or %d or %o) and all %s/%S/%Q parameters are strings. 238 * Result: allocates a new string containing the sprintf result. 239 * The %S parameters are checked for being shell safe. 240 * The %Q parameters are checked for being shell safe inside single quotes. 241 */ 242 243static int 244is_shell_safe(const char *ss, int quoted) { 245 char *bad = " ;'\\\"<>|"; 246 char *p; 247 248 if (quoted) 249 bad++; /* allow a space inside quotes */ 250 for (p = bad; *p; p++) 251 if (strchr(ss, *p)) 252 return 0; 253 return 1; 254} 255 256static void 257nothing(int x) {} 258 259char * 260my_xsprintf (char *format, ...) { 261 va_list p; 262 char *s, *ss, *fm; 263 int len; 264 265 len = strlen(format) + 1; 266 fm = my_strdup(format); 267 268 va_start(p, format); 269 for (s = fm; *s; s++) { 270 if (*s == '%') { 271 switch (s[1]) { 272 case 'Q': 273 case 'S': /* check and turn into 's' */ 274 ss = va_arg(p, char *); 275 if (!is_shell_safe(ss, (s[1] == 'Q'))) 276 return my_strdup(NOT_SAFE); 277 len += strlen(ss); 278 s[1] = 's'; 279 break; 280 case 's': 281 len += strlen(va_arg(p, char *)); 282 break; 283 case 'd': 284 case 'o': 285 case 'c': 286 len += 20; 287 nothing(va_arg(p, int)); /* advance */ 288 break; 289 default: 290 fprintf(stderr, 291 "my_xsprintf called with %s\n", 292 format); 293 exit(1); 294 } 295 } 296 } 297 va_end(p); 298 299 s = my_malloc(len); 300 va_start(p, format); 301 vsprintf(s, fm, p); 302 va_end(p); 303 304 return s; 305} 306