1 /*
2 * binkleyforce -- unix FTN mailer project
3 *
4 * Copyright (c) 1998-2000 Alexander Belkin, 2:5020/1398.11
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * $Id: os_unix.c,v 1.1.1.1 2004/09/09 09:52:38 kstepanenkov Exp $
12 */
13
14 #include "includes.h"
15 #include "confread.h"
16 #include "logger.h"
17
18 struct {
19 char *name;
20 int value;
21 } exec_options_names[] = {
22 { "nowait", EXEC_OPT_NOWAIT },
23 { "setsid", EXEC_OPT_SETSID },
24 { "logout", EXEC_OPT_LOGOUT },
25 { "useshell", EXEC_OPT_USESHELL },
26 { NULL, 0 }
27 };
28
exec_file_exist(const char * command)29 int exec_file_exist(const char *command)
30 {
31 const char *p;
32
33 ASSERT(command);
34
35 if( *command == '[' )
36 {
37 p = strchr(command + 1, ']');
38 if( !p )
39 return -1;
40 ++p;
41 }
42 else
43 p = command;
44
45 if( !access(p, X_OK) )
46 return 0;
47
48 return -1;
49 }
50
exec_options_parse(char * str)51 int exec_options_parse(char *str)
52 {
53 int yield = 0;
54 char *p;
55 char *n;
56 int i;
57
58 for( p = string_token(str, &n, ", \t", 0); p;
59 p = string_token(NULL, &n, ", \t", 0) )
60 {
61 for( i = 0; exec_options_names[i].name; i++ )
62 if( !strcasecmp(p, exec_options_names[i].name) )
63 {
64 yield |= exec_options_names[i].value;
65 break;
66 }
67 if( !exec_options_names[i].name )
68 bf_log("unknown exec option '%s'", p);
69 }
70
71 return yield;
72 }
73
exec_options_init(s_exec_options * eopt)74 void exec_options_init(s_exec_options *eopt)
75 {
76 memset(eopt, '\0', sizeof(s_exec_options));
77 eopt->umask = EXEC_DEFAULT_UMASK;
78 eopt->envp[0] = NULL;
79 }
80
exec_options_deinit(s_exec_options * eopt)81 void exec_options_deinit(s_exec_options *eopt)
82 {
83 int i;
84
85 if( eopt->command )
86 free(eopt->command);
87
88 for( i = 0; eopt->envp[i]; i++ )
89 free(eopt->envp[i]);
90
91 memset(eopt, '\0', sizeof(s_exec_options));
92 }
93
exec_env_add(s_exec_options * eopt,const char * name,const char * value)94 void exec_env_add(s_exec_options *eopt, const char *name, const char *value)
95 {
96 int i;
97
98 for( i = 0; eopt->envp[i]; i++ ); /* Empty Loop */
99
100 if( i < EXEC_MAX_NUM_ENVS )
101 {
102 eopt->envp[i] = xmalloc(strlen(name) + strlen(value) + 2);
103 sprintf(eopt->envp[i], "%s=%s", name, value);
104 eopt->envp[i+1] = NULL;
105 }
106 }
107
exec_options_set_command(s_exec_options * eopt,const char * command)108 void exec_options_set_command(s_exec_options *eopt, const char *command)
109 {
110 char *copy, *p;
111
112 if( *command == '[' )
113 {
114 copy = xstrcpy(command);
115 p = strchr(copy, ']');
116 if( !p )
117 eopt->command = copy;
118 else
119 {
120 *p++ = '\0';
121 eopt->command = xstrcpy(string_trimleft(p));
122 eopt->options = exec_options_parse(copy+1);
123 free(copy);
124 }
125 }
126 else
127 eopt->command = xstrcpy(command);
128 }
129
exec_redirect_descriptor(int desc,const char * fname,int flags)130 int exec_redirect_descriptor(int desc, const char *fname, int flags)
131 {
132 int fd;
133
134 (void)close(desc);
135
136 if( (fd = open(fname, flags, S_IRUSR|S_IWUSR)) == -1 )
137 {
138 logerr("exec error: cannot open \"%s\"", fname);
139 return -1;
140 }
141
142 if( fd != desc )
143 {
144 bf_log("exec error: cannot open \"%s\" <--> %d (got %d)",
145 fname, desc, fd);
146 return -1;
147 }
148
149 return 0;
150 }
151
exec_command(s_exec_options * eopt)152 int exec_command(s_exec_options *eopt)
153 {
154 pid_t pid = 0;
155 int status = 0;
156 time_t starttime = 0;
157 time_t timer = 0;
158 char *tempfile = NULL;
159 struct sigaction new_chld;
160 struct sigaction old_chld;
161 sigset_t new_mask;
162 sigset_t old_mask;
163
164 if( eopt->command == NULL )
165 return -1;
166
167 if( eopt->options & EXEC_OPT_LOGOUT )
168 {
169 tempfile = file_gettmp();
170 if( !tempfile )
171 bf_log("cannot generate temporary file name");
172 }
173
174 /* Block SIGCHLD */
175 sigemptyset(&new_mask);
176 sigaddset(&new_mask, SIGCHLD);
177 sigprocmask(SIG_BLOCK, &new_mask, NULL);
178
179 /* Set the default SIGCHLD handler */
180 new_chld.sa_handler = SIG_DFL;
181 sigaction(SIGCHLD, &new_chld, &old_chld);
182
183 if( (pid = fork()) == -1 )
184 {
185 logerr("exec error: cannot fork()");
186 return -1;
187 }
188
189 if( !pid )
190 {
191 if( exec_redirect_descriptor(0, "/dev/null", O_RDONLY) == -1 )
192 exit(128);
193
194 if( tempfile )
195 {
196 if( exec_redirect_descriptor(1, tempfile, O_WRONLY|O_CREAT|O_TRUNC) == -1
197 || exec_redirect_descriptor(2, tempfile, O_WRONLY) == -1 )
198 exit(128);
199 }
200 else
201 {
202 if( exec_redirect_descriptor(1, "/dev/null", O_WRONLY) == -1
203 || exec_redirect_descriptor(2, "/dev/null", O_WRONLY) == -1 )
204 exit(128);
205 }
206
207 if( eopt->options & EXEC_OPT_SETSID )
208 setsid();
209
210 if( eopt->options & EXEC_OPT_USESHELL )
211 execle("/bin/sh", "sh", "-c", eopt->command, NULL, eopt->envp);
212 else
213 {
214 char *argv[EXEC_MAX_NUM_ARGS+1];
215 int argc = string_parse_regular(argv, EXEC_MAX_NUM_ARGS,
216 eopt->command);
217 if( argc > 0 )
218 {
219 argv[argc] = NULL;
220 execve(argv[0], argv, eopt->envp);
221 }
222 /* eopt->command is corrupted now */
223 }
224
225 /* We get here only in case of errors */
226 logerr("cannot execute \"%s\"", eopt->command);
227
228 exit(128);
229 }
230
231 /*
232 * We are inside parent process, do:
233 *
234 * 1) if OUTMODE_LOGPIPE is used than log child process ouput
235 * 2) wait for child process to exit
236 */
237
238 bf_log("running \"%s\", PID %d",
239 string_printable(eopt->command), (int)pid);
240
241 if( !(eopt->options & EXEC_OPT_NOWAIT) )
242 {
243 starttime = time(NULL); /* Set process start time */
244
245 if( waitpid(pid, &status, 0) > 0 )
246 {
247 eopt->runtime = time(NULL) - starttime;
248
249 if( eopt->runtime < 0 )
250 eopt->runtime = 0;
251
252 /*
253 * Write process's output to the log file
254 */
255 if( tempfile )
256 {
257 char buf[256];
258 FILE *fp = file_open(tempfile, "r");
259
260 if( !fp )
261 logerr("cannot open temporary file \"%s\"", tempfile);
262 else
263 {
264 while( fgets(buf, sizeof(buf), fp) )
265 {
266 string_chomp(buf);
267 bf_log("[%d] %s", pid, string_printable(buf));
268 }
269 file_close(fp);
270 }
271 }
272
273 if( WIFEXITED(status) )
274 {
275 eopt->retc = WEXITSTATUS(status);
276 bf_log("process %d exit with code %d (%d seconds)",
277 (int)pid, eopt->retc, eopt->runtime);
278 }
279 else if( WIFSIGNALED(status) )
280 {
281 eopt->retc = -1;
282 bf_log("process %d terminated on signal %d (%d seconds)",
283 (int)pid, WTERMSIG(status), eopt->runtime);
284 }
285 else
286 {
287 eopt->retc = -1;
288 bf_log("process %d return with unknown status (%d seconds)",
289 (int)pid, eopt->runtime);
290 }
291 }
292 else
293 {
294 eopt->retc = -1;
295 logerr("waitpid return error for PID %d", (int)pid);
296 }
297 }
298
299 if( tempfile )
300 {
301 if( unlink(tempfile) == -1 && errno != ENOENT )
302 logerr("cannot unlink temporary file \"%s\"", tempfile);
303 free(tempfile);
304 }
305
306 /* Restore the original SIGCHLD handler */
307 sigaction(SIGCHLD, &old_chld, NULL);
308
309 /* Unblock SIGCHLD */
310 sigprocmask(SIG_UNBLOCK, &new_mask, NULL);
311
312 return eopt->retc;
313 }
314
315 /*
316 * Return -1 if error occured while excuting command, other values
317 * are return codes of your command
318 */
xsystem(const char * command,const char * p_input,const char * p_output)319 int xsystem(const char *command, const char *p_input, const char *p_output)
320 {
321 pid_t pid;
322 int status;
323
324 ASSERT(command != NULL);
325
326 DEB((D_INFO, "xsystem: command \"%s\", input \"%s\", output \"%s\"",
327 command, p_input, p_output));
328
329 switch(pid=fork()) {
330 case -1:
331 return(-1);
332 case 0:
333 if( p_input )
334 {
335 close(0);
336 if( open(p_input, O_RDONLY) != 0 )
337 {
338 logerr("can't open stdin \"%s\"", p_input);
339 exit(-1);
340 }
341 }
342 if( p_output )
343 {
344 close(1);
345 if( open(p_output, O_WRONLY|O_APPEND|O_CREAT, 0600) != 1 )
346 {
347 logerr("can't open stdout \"%s\"", p_output);
348 exit(-1);
349 }
350 }
351 if( p_output )
352 {
353 close(2);
354 if( open(p_output, O_WRONLY|O_APPEND|O_CREAT, 0600) != 2 )
355 {
356 logerr("can't open stderr \"%s\"", p_output);
357 exit(-1);
358 }
359 }
360 #ifdef SHELL
361 execl(SHELL, "sh", "-c", command, NULL);
362 #else
363 execl("/bin/sh", "sh", "-c", command, NULL);
364 #endif
365 exit( (errno == 0)?0:-1 );
366 }
367
368 if( waitpid(pid, &status, 0) == pid && WIFEXITED(status) )
369 {
370 return(WEXITSTATUS(status));
371 }
372 return(-1);
373 }
374
375 #ifndef HAVE_RENAME
rename(const char * old_name,const char * new_name)376 int rename(const char *old_name, const char *new_name)
377 {
378 int rc;
379
380 ASSERT(old_name != NULL && new_name != NULL);
381
382 if( (rc = link(old_name, new_name)) == 0 )
383 {
384 if( (rc = unlink(old_name)) == -1 )
385 { logerr("can't unlink file \"%s\"", old_name); }
386 return 0;
387 }
388 else
389 {
390 return -1;
391 }
392 }
393 #endif
394
395 /* ------------------------------------------------------------------------- */
396 /* Get free space on device mounted at path $path */
397 /* ------------------------------------------------------------------------- */
398 #if defined(HAVE_STATFS) || defined(HAVE_STATVFS)
getfreespace(const char * path)399 size_t getfreespace(const char *path)
400 {
401 #ifdef HAVE_STATVFS
402 struct statvfs sfs;
403 #else
404 struct statfs sfs;
405 #endif
406
407 ASSERT(path != NULL);
408
409 #ifdef HAVE_STATVFS
410 if( statvfs(path, &sfs) == 0 )
411 #else
412 if( statfs(path, &sfs) == 0 )
413 #endif
414 {
415 return(sfs.f_bsize * sfs.f_bavail);
416 }
417 else
418 {
419 logerr("can't statfs \"%s\", assume enough space", path);
420 return(~0L);
421 }
422 }
423 #else
getfreespace(const char * path)424 size_t getfreespace(const char *path)
425 {
426 ASSERT(path != NULL);
427
428 bf_log("warning: fake getfreespace - assume enough space");
429 return(~0L);
430 }
431 #endif
432
433 #ifndef HAVE_SETPROCTITLE
434
435 /*
436 * clobber argv so ps will show what we're doing.
437 * (stolen from BSD ftpd where it was stolen from sendmail)
438 * warning, since this is usually started from inetd.conf, it
439 * often doesn't have much of an environment or arglist to overwrite.
440 */
441
442 static char *cmdstr = NULL;
443 static char *cmdstrend = NULL;
444
setargspace(char * argv[],char * envp[])445 void setargspace(char *argv[], char *envp[])
446 {
447 cmdstr = argv[0];
448 while( *envp ) envp++;
449 envp--;
450 cmdstrend = (*envp) + strlen(*envp);
451 }
452
setproctitle(const char * fmt,...)453 void setproctitle(const char *fmt, ...)
454 {
455 va_list args;
456 char buf[256]; /* I hope it will be large enough */
457 char *d = cmdstr;
458 char *s = buf;
459
460 va_start(args, fmt);
461 vsprintf(buf, fmt, args);
462 va_end(args);
463
464 /* Make ps print our process name */
465 while( d < cmdstrend && *s ) { *d = *s; d++; s++; }
466 while( d < cmdstrend ) *d++ = ' ';
467 }
468
469 #endif /* HAVE_SETPROCTITLE */
470