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