Commit | Line | Data |
---|---|---|
32c0721d WJ |
1 | /* sys1.unx |
2 | The basic system dependent routines for UNIX. | |
3 | ||
4 | Copyright (C) 1991, 1992 Ian Lance Taylor | |
5 | ||
6 | This file is part of the Taylor UUCP package. | |
7 | ||
8 | This program is free software; you can redistribute it and/or | |
9 | modify it under the terms of the GNU General Public License as | |
10 | published by the Free Software Foundation; either version 2 of the | |
11 | License, or (at your option) any later version. | |
12 | ||
13 | This program is distributed in the hope that it will be useful, but | |
14 | WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
16 | General Public License for more details. | |
17 | ||
18 | You should have received a copy of the GNU General Public License | |
19 | along with this program; if not, write to the Free Software | |
20 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
21 | ||
22 | The author of the program may be contacted at ian@airs.com or | |
23 | c/o AIRS, P.O. Box 520, Waltham, MA 02254. | |
24 | ||
25 | $Log: sys1.unx,v $ | |
26 | # Revision 1.2 92/05/13 05:42:07 rich | |
27 | # ported to 386bsd | |
28 | # | |
29 | # Revision 1.1 1992/05/10 17:36:33 rich | |
30 | # Initial revision | |
31 | # | |
32 | Revision 1.68 1992/04/03 05:37:11 ian | |
33 | Minor cleanups for gcc 2.1 | |
34 | ||
35 | Revision 1.67 1992/03/31 23:53:34 ian | |
36 | Use $PWD to get the current directory if it's defined and correct | |
37 | ||
38 | Revision 1.66 1992/03/31 23:42:59 ian | |
39 | Brian W. Antoine: use name from getpwnam rather than getlogin | |
40 | ||
41 | Revision 1.65 1992/03/26 17:17:25 ian | |
42 | Gerben Wierda: various cleanups | |
43 | ||
44 | Revision 1.64 1992/03/18 06:00:25 ian | |
45 | Open the controlling terminal in non delay mode | |
46 | ||
47 | Revision 1.63 1992/03/17 15:35:28 ian | |
48 | Log signals when they happen, even if we continue looping | |
49 | ||
50 | Revision 1.62 1992/03/17 01:28:18 ian | |
51 | Undefine remove in uucp.h if ! HAVE_REMOVE | |
52 | ||
53 | Revision 1.61 1992/03/16 22:40:01 ian | |
54 | Undefine remove before function definition | |
55 | ||
56 | Revision 1.60 1992/03/16 22:22:35 ian | |
57 | Adjusted external declarations | |
58 | ||
59 | Revision 1.59 1992/03/16 22:01:58 ian | |
60 | Don't declare sigemptyset | |
61 | ||
62 | Revision 1.58 1992/03/16 01:23:08 ian | |
63 | Make blocking writes optional | |
64 | ||
65 | Revision 1.57 1992/03/15 04:51:17 ian | |
66 | Keep an array of signals we've received rather than a single variable | |
67 | ||
68 | Revision 1.56 1992/03/15 01:54:46 ian | |
69 | All execs are now done in isspawn, all waits are done in iswait | |
70 | ||
71 | Revision 1.55 1992/03/12 19:54:43 ian | |
72 | Debugging based on types rather than number | |
73 | ||
74 | Revision 1.54 1992/03/11 02:09:57 ian | |
75 | Franc,ois Pinard: retry fork several times before giving up | |
76 | ||
77 | Revision 1.53 1992/03/11 00:18:50 ian | |
78 | Save temporary file if file send fails | |
79 | ||
80 | Revision 1.52 1992/03/08 02:06:28 ian | |
81 | Let setpgrp fail silently | |
82 | ||
83 | Revision 1.51 1992/03/04 01:40:51 ian | |
84 | Thomas Fischer: tweaked a bit for the NeXT | |
85 | ||
86 | Revision 1.50 1992/03/03 21:01:20 ian | |
87 | Use strict timeout in fsserial_read, eliminate all race conditions | |
88 | ||
89 | Revision 1.49 1992/02/29 01:06:59 ian | |
90 | Chip Salzenberg: recheck file permissions before sending | |
91 | ||
92 | Revision 1.48 1992/02/28 05:06:15 ian | |
93 | T. William Wells: fsysdep_catch must be a macro | |
94 | ||
95 | Revision 1.47 1992/02/27 19:51:09 ian | |
96 | Added some new extern definitions | |
97 | ||
98 | Revision 1.46 1992/02/27 05:40:54 ian | |
99 | T. William Wells: detach from controlling terminal, handle signals safely | |
100 | ||
101 | Revision 1.45 1992/02/24 20:07:43 ian | |
102 | John Theus: some systems don't have <fcntl.h> | |
103 | ||
104 | Revision 1.44 1992/02/24 04:58:47 ian | |
105 | Only permit files to be received into directories that are world-writeable | |
106 | ||
107 | Revision 1.43 1992/02/23 03:26:51 ian | |
108 | Overhaul to use automatic configure shell script | |
109 | ||
110 | Revision 1.42 1992/02/19 19:36:07 ian | |
111 | Rearranged time functions | |
112 | ||
113 | Revision 1.41 1992/02/09 05:10:50 ian | |
114 | Added HAVE_MKDIR configuration parameter and mkdir emulation | |
115 | ||
116 | Revision 1.40 1992/02/09 03:14:48 ian | |
117 | Added HAVE_OLD_DIRECTORIES for systems without readdir routines | |
118 | ||
119 | Revision 1.39 1992/02/09 02:41:58 ian | |
120 | Added HAVE_DUP2 configuration parameter and dup2 emulation function | |
121 | ||
122 | Revision 1.38 1992/02/08 23:38:17 ian | |
123 | Put utsname on stack rather than making it static | |
124 | ||
125 | Revision 1.37 1992/02/08 23:34:41 ian | |
126 | If we have neither getcwd nor getwd, fork /bin/pwd to get the cwd | |
127 | ||
128 | Revision 1.36 1992/02/08 22:33:32 ian | |
129 | Only get the current working directory if it's going to be needed | |
130 | ||
131 | Revision 1.35 1992/02/08 03:54:18 ian | |
132 | Include <string.h> only in <uucp.h>, added 1992 copyright | |
133 | ||
134 | Revision 1.34 1992/01/29 04:27:11 ian | |
135 | Jay Vassos-Libove: removed some conflicting declarations | |
136 | ||
137 | Revision 1.33 1992/01/22 05:08:21 ian | |
138 | Call execl with correct first argument | |
139 | ||
140 | Revision 1.32 1992/01/21 19:39:12 ian | |
141 | Chip Salzenberg: uucp and uux start uucico for right system, not any | |
142 | ||
143 | Revision 1.31 1992/01/21 00:30:48 ian | |
144 | Don't try to create a directory with no name | |
145 | ||
146 | Revision 1.30 1992/01/16 03:38:20 ian | |
147 | Put \n at end of fsysdep_run error message | |
148 | ||
149 | Revision 1.29 1992/01/15 21:06:11 ian | |
150 | Mike Park: some systems can't include <sys/time.h> and <time.h> together | |
151 | ||
152 | Revision 1.28 1992/01/13 19:38:16 ian | |
153 | Chip Salzenberg: can't declare execl, since it is varadic | |
154 | ||
155 | Revision 1.27 1992/01/13 06:11:39 ian | |
156 | David Nugent: can't declare open or fcntl | |
157 | ||
158 | Revision 1.26 1992/01/11 17:30:10 ian | |
159 | John Antypas: use memcpy instead of relying on structure assignment | |
160 | ||
161 | Revision 1.25 1992/01/04 23:23:57 ian | |
162 | usysdep_localtime can't use usysdep_full_time if HAVE_TIMES | |
163 | ||
164 | Revision 1.24 1992/01/04 22:56:22 ian | |
165 | Added extern definition | |
166 | ||
167 | Revision 1.23 1991/12/29 15:45:46 ian | |
168 | Don't take the address of a cast value | |
169 | ||
170 | Revision 1.22 1991/12/29 04:04:18 ian | |
171 | Added a bunch of extern definitions | |
172 | ||
173 | Revision 1.21 1991/12/29 02:59:50 ian | |
174 | Lele Gaifax: put full year in log file | |
175 | ||
176 | Revision 1.20 1991/12/28 17:08:47 ian | |
177 | John Theus: offer HAVE_GETWD as an alternative to using getcwd | |
178 | ||
179 | Revision 1.19 1991/12/28 07:01:15 ian | |
180 | Added HAVE_FTIME configuration option | |
181 | ||
182 | Revision 1.18 1991/12/22 22:14:19 ian | |
183 | Monty Solomon: added HAVE_UNISTD_H configuration parameter | |
184 | ||
185 | Revision 1.17 1991/12/21 21:34:14 ian | |
186 | Moved fsysdep_file_exists from sys5.unx to sys1.unx | |
187 | ||
188 | Revision 1.16 1991/12/21 21:04:42 ian | |
189 | Use real program name in fsysdep_run error messages | |
190 | ||
191 | Revision 1.15 1991/12/17 07:09:58 ian | |
192 | Record statistics in fractions of a second | |
193 | ||
194 | Revision 1.14 1991/12/12 18:35:47 ian | |
195 | Do locking with link to avoid races and to permit running as root | |
196 | ||
197 | Revision 1.13 1991/12/12 17:39:40 ian | |
198 | Set the GID as well as the UID for extra safety | |
199 | ||
200 | Revision 1.12 1991/12/11 03:59:19 ian | |
201 | Create directories when necessary; don't just assume they exist | |
202 | ||
203 | Revision 1.11 1991/12/06 22:50:01 ian | |
204 | Franc,ois Pinard: getcwd may legitimately fail in usysdep_initialize | |
205 | ||
206 | Revision 1.10 1991/12/01 02:23:12 ian | |
207 | Niels Baggesen: don't multiply include <unistd.h> | |
208 | ||
209 | Revision 1.9 1991/11/21 20:59:32 ian | |
210 | Brian Campbell: ttyname takes an argument | |
211 | ||
212 | Revision 1.8 1991/11/14 19:11:25 ian | |
213 | Add extern for ttyname | |
214 | ||
215 | Revision 1.7 1991/11/14 03:40:10 ian | |
216 | Try to figure out whether stdin is a TCP port | |
217 | ||
218 | Revision 1.6 1991/11/11 18:55:52 ian | |
219 | Get protocol parameters from port and dialer for incoming calls | |
220 | ||
221 | Revision 1.5 1991/09/19 17:49:39 ian | |
222 | Chip Salzenberg: the log file has been closed before calling fsysdep_run | |
223 | ||
224 | Revision 1.4 1991/09/19 15:46:48 ian | |
225 | Chip Salzenberg: Make sure getlogin () uid matches process uid | |
226 | ||
227 | Revision 1.3 1991/09/19 03:23:34 ian | |
228 | Chip Salzenberg: append to private debugging file, don't overwrite it | |
229 | ||
230 | Revision 1.2 1991/09/11 02:33:14 ian | |
231 | Added ffork argument to fsysdep_run | |
232 | ||
233 | Revision 1.1 1991/09/10 19:45:50 ian | |
234 | Initial revision | |
235 | ||
236 | */ | |
237 | ||
238 | #include "uucp.h" | |
239 | ||
240 | #if USE_RCS_ID | |
241 | char sys1_unx_rcsid[] = "$Id: sys1.unx,v 1.2 92/05/13 05:42:07 rich Exp Locker: root $"; | |
242 | #endif | |
243 | ||
244 | #include <errno.h> | |
245 | ||
246 | #if USE_STDIO && HAVE_UNISTD_H | |
247 | #include <unistd.h> | |
248 | #endif | |
249 | ||
250 | #include "system.h" | |
251 | #include "sysdep.h" | |
252 | ||
253 | #include <pwd.h> | |
254 | ||
255 | #if HAVE_GETGRENT | |
256 | #include <grp.h> | |
257 | extern struct group *getgrent (); | |
258 | #endif | |
259 | ||
260 | #if HAVE_LIMITS_H | |
261 | #include <limits.h> | |
262 | #endif | |
263 | ||
264 | #if HAVE_SYS_PARAM_H | |
265 | #include <sys/param.h> | |
266 | #endif | |
267 | ||
268 | #if HAVE_FCNTL_H | |
269 | #include <fcntl.h> | |
270 | #else | |
271 | #if HAVE_SYS_FILE_H | |
272 | #include <sys/file.h> | |
273 | #endif | |
274 | #endif | |
275 | ||
276 | #ifndef O_RDONLY | |
277 | #define O_RDONLY 0 | |
278 | #define O_WRONLY 1 | |
279 | #define O_RDWR 2 | |
280 | #endif | |
281 | ||
282 | #ifndef O_APPEND | |
283 | #ifdef FAPPEND | |
284 | #define O_APPEND FAPPEND | |
285 | #endif | |
286 | #endif | |
287 | ||
288 | #ifndef O_NOCTTY | |
289 | #define O_NOCTTY 0 | |
290 | #endif | |
291 | ||
292 | #if ! HAVE_GETHOSTNAME && HAVE_UNAME | |
293 | #include <sys/utsname.h> | |
294 | extern int uname (); | |
295 | #endif | |
296 | ||
297 | #if HAVE_TIME_H && (HAVE_SYS_TIME_AND_TIME_H || ! HAVE_GETTIMEOFDAY) | |
298 | #include <time.h> | |
299 | #endif | |
300 | ||
301 | #if HAVE_SYS_IOCTL_H | |
302 | #include <sys/ioctl.h> | |
303 | #endif | |
304 | ||
305 | /* If we have getcwd, forget about getwd. */ | |
306 | ||
307 | #if HAVE_GETCWD | |
308 | #undef HAVE_GETWD | |
309 | #define HAVE_GETWD 0 | |
310 | #endif | |
311 | ||
312 | #if HAVE_GETWD | |
313 | /* If we didn't get MAXPATHLEN, make it up. */ | |
314 | #ifndef MAXPATHLEN | |
315 | #define MAXPATHLEN 1024 | |
316 | #endif | |
317 | extern char *getwd (); | |
318 | #endif /* HAVE_GETWD */ | |
319 | ||
320 | /* Prefer gettimeofday to ftime to times. */ | |
321 | ||
322 | #if HAVE_GETTIMEOFDAY || HAVE_FTIME | |
323 | #undef HAVE_TIMES | |
324 | #define HAVE_TIMES 0 | |
325 | #endif | |
326 | ||
327 | #if HAVE_GETTIMEOFDAY | |
328 | #undef HAVE_FTIME | |
329 | #define HAVE_FTIME 0 | |
330 | #endif | |
331 | ||
332 | #if HAVE_GETTIMEOFDAY | |
333 | #include <sys/time.h> | |
334 | extern int gettimeofday (); | |
335 | #endif | |
336 | ||
337 | #if HAVE_FTIME | |
338 | #include <sys/timeb.h> | |
339 | extern int ftime (); | |
340 | #endif | |
341 | ||
342 | #if HAVE_TIMES | |
343 | #include <sys/times.h> | |
344 | #if TIMES_DECLARATION_OK | |
345 | /* We use a macro to protect this because times really returns clock_t | |
346 | and on some systems, such as Ultrix 4.0, clock_t is int. We don't | |
347 | leave it out entirely because on some systems, such as System III, | |
348 | the declaration is necessary for correct compilation. */ | |
349 | extern long times (); | |
350 | #endif | |
351 | ||
352 | #if TIMES_TICK == 0 | |
353 | /* We don't have a value for TIMES_TICK. Look for one. */ | |
354 | #ifdef CLK_TCK | |
355 | #undef TIMES_TICK | |
356 | #define TIMES_TICK CLK_TCK | |
357 | #else /* ! defined (CLK_TCK) */ | |
358 | #ifdef HZ | |
359 | #undef TIMES_TICK | |
360 | #define TIMES_TICK HZ | |
361 | #endif /* defined (HZ) */ | |
362 | #endif /* ! defined (CLK_TCK) */ | |
363 | #endif /* TIMES_TICK == 0 */ | |
364 | ||
365 | #endif /* HAVE_TIMES */ | |
366 | ||
367 | /* We need the access macros. */ | |
368 | ||
369 | #ifndef R_OK | |
370 | #define R_OK 4 | |
371 | #define W_OK 2 | |
372 | #define X_OK 1 | |
373 | #define F_OK 0 | |
374 | #endif /* ! defined (R_OK) */ | |
375 | ||
376 | /* We need wait status information. */ | |
377 | ||
378 | #if HAVE_SYS_WAIT_H | |
379 | #include <sys/wait.h> | |
380 | #endif | |
381 | ||
382 | /* We use a typedef wait_status for wait and related functions to put | |
383 | results into. We define the POSIX examination functions we need if | |
384 | they are not already defined (if they aren't defined, I assume that | |
385 | we have a standard wait status). */ | |
386 | ||
387 | #if HAVE_UNION_WAIT | |
388 | typedef union wait wait_status; | |
389 | #ifndef WIFEXITED | |
390 | #define WIFEXITED(u) ((u).w_termsig == 0) | |
391 | #endif | |
392 | #ifndef WEXITSTATUS | |
393 | #define WEXITSTATUS(u) ((u).w_retcode) | |
394 | #endif | |
395 | #ifndef WTERMSIG | |
396 | #define WTERMSIG(u) ((u).w_termsig) | |
397 | #endif | |
398 | #else /* ! HAVE_UNION_WAIT */ | |
399 | typedef int wait_status; | |
400 | #ifndef WIFEXITED | |
401 | #define WIFEXITED(i) (((i) & 0xff) == 0) | |
402 | #endif | |
403 | #ifndef WEXITSTATUS | |
404 | #define WEXITSTATUS(i) (((i) >> 8) & 0xff) | |
405 | #endif | |
406 | #ifndef WTERMSIG | |
407 | #define WTERMSIG(i) ((i) & 0x7f) | |
408 | #endif | |
409 | #endif /* ! HAVE_UNION_WAIT */ | |
410 | ||
411 | /* External variables. */ | |
412 | extern char **environ; | |
413 | ||
414 | /* External functions. */ | |
415 | #ifndef __386BSD__ | |
416 | extern int kill (); | |
417 | #endif __386BSD__ | |
418 | extern int chdir (), access (), stat (), unlink (), execve (); | |
419 | extern int close (), pipe (), dup2 (); | |
420 | extern int fputs (); | |
421 | extern void _exit (); | |
422 | extern time_t time (); | |
423 | extern char *getlogin (), *ttyname (); | |
424 | extern pid_t getpid (), getppid (), fork (), getpgrp (); | |
425 | extern uid_t getuid (), geteuid (), getgid (), getegid (); | |
426 | extern struct tm *localtime (); | |
427 | #ifndef __386BSD__ | |
428 | extern struct passwd *getpwuid (); | |
429 | #endif __386BSD__ | |
430 | extern struct passwd *getpwnam (); | |
431 | #if HAVE_GETHOSTNAME | |
432 | extern int gethostname (); | |
433 | #endif | |
434 | #if HAVE_GETCWD | |
435 | extern char *getcwd (); | |
436 | #else | |
437 | #if ! HAVE_GETWD | |
438 | static char *getcwd P((char *zbuf, int cbuf)); | |
439 | #endif /* ! HAVE_GETWD */ | |
440 | #endif /* ! HAVE_GETCWD */ | |
441 | #if HAVE_GETDTABLESIZE | |
442 | extern int getdtablesize (); | |
443 | #endif | |
444 | #if HAVE_SYSCONF | |
445 | extern long sysconf (); | |
446 | #endif | |
447 | #if HAVE_SETPGRP | |
448 | #ifndef __386BSD__ | |
449 | extern int setpgrp (); | |
450 | #endif __386BSD__ | |
451 | #endif | |
452 | #if HAVE_SETSID | |
453 | #ifndef __386BSD__ | |
454 | extern int setsid (); | |
455 | #endif __386BSD__ | |
456 | #endif | |
457 | #if HAVE_SIGACTION | |
458 | extern int sigaction (); | |
459 | #endif | |
460 | #if HAVE_SIGVEC | |
461 | extern int sigvec (); | |
462 | #endif | |
463 | #if HAVE_TCP | |
464 | extern int getsockname (); | |
465 | #endif | |
466 | \f | |
467 | /* Initialize the system dependent routines. We will probably be running | |
468 | suid to uucp, so we make sure that nothing is obviously wrong. We | |
469 | save the login name since we will be losing the real uid. */ | |
470 | static char *zSlogin; | |
471 | ||
472 | /* We save the current directory since we will do a chdir to the | |
473 | spool directory. */ | |
474 | char *zScwd; | |
475 | ||
476 | /* The maximum length of a system name is controlled by the type of spool | |
477 | directory we use. */ | |
478 | ||
479 | #if SPOOLDIR_V2 | SPOOLDIR_BSD42 | SPOOLDIR_BSD43 | SPOOLDIR_ULTRIX | |
480 | int cSysdep_max_name_len = 7; | |
481 | #endif /* SPOOLDIR_V2 | SPOOLDIR_BSD42 | SPOOLDIR_BSD43 | SPOOLDIR_ULTRIX */ | |
482 | #if SPOOLDIR_BNU | |
483 | int cSysdep_max_name_len = 14; | |
484 | #endif /* SPOOLDIR_BNU */ | |
485 | #if SPOOLDIR_TAYLOR | |
486 | #if HAVE_LONG_NAMES | |
487 | int cSysdep_max_name_len = 255; | |
488 | #else /* ! HAVE_LONG_NAMES */ | |
489 | int cSysdep_max_name_len = 14; | |
490 | #endif /* ! HAVE_LONG_NAMES */ | |
491 | #endif /* SPOOLDIR_TAYLOR */ | |
492 | ||
493 | /* The number of available file descriptors. */ | |
494 | ||
495 | static int cSdescriptors; | |
496 | ||
497 | /* Local functions. */ | |
498 | ||
499 | static void xmkdir P((const char *zdir)); | |
500 | static void usmake_spool_dir P((void)); | |
501 | \f | |
502 | void | |
503 | usysdep_initialize (fdaemon, fgetcwd) | |
504 | boolean fdaemon; | |
505 | boolean fgetcwd; | |
506 | { | |
507 | int o; | |
508 | char *z; | |
509 | struct passwd *q; | |
510 | ||
511 | ulog_id (getpid ()); | |
512 | ||
513 | #if HAVE_GETDTABLESIZE | |
514 | cSdescriptors = getdtablesize (); | |
515 | #else | |
516 | #if HAVE_SYSCONF | |
517 | cSdescriptors = sysconf (_SC_OPEN_MAX); | |
518 | #else | |
519 | #ifdef OPEN_MAX | |
520 | cSdescriptors = OPEN_MAX; | |
521 | #else | |
522 | #ifdef NOFILE | |
523 | cSdescriptors = NOFILE; | |
524 | #else | |
525 | cSdescriptors = 20; | |
526 | #endif /* ! defined (NOFILE) */ | |
527 | #endif /* ! defined (OPEN_MAX) */ | |
528 | #endif /* ! HAVE_SYSCONF */ | |
529 | #endif /* ! HAVE_GETDTABLESIZE */ | |
530 | ||
531 | /* Close everything but stdin, stdout and stderr. */ | |
532 | ||
533 | for (o = 3; o < cSdescriptors; o++) | |
534 | (void) close (o); | |
535 | ||
536 | /* Make sure stdin, stdout and stderr are open. Otherwise, newly | |
537 | opened files will appear to be them and confusion will result. */ | |
538 | if (fcntl (0, F_GETFD, 0) == -1 | |
539 | && open ("/dev/null", O_RDONLY, 0) != 0) | |
540 | exit (EXIT_FAILURE); | |
541 | if (fcntl (1, F_GETFD, 0) == -1 | |
542 | && open ("/dev/null", O_WRONLY, 0) != 1) | |
543 | exit (EXIT_FAILURE); | |
544 | if (fcntl (2, F_GETFD, 0) == -1 | |
545 | && open ("/dev/null", O_WRONLY, 0) != 2) | |
546 | exit (EXIT_FAILURE); | |
547 | ||
548 | /* We always set our file modes to exactly what we want. */ | |
549 | umask (0); | |
550 | ||
551 | /* Get the login name, making sure that it matches the uid. Many | |
552 | systems truncate the getlogin return value to 8 characters, but | |
553 | keep the full name in the password file, so we prefer the name in | |
554 | the password file. */ | |
555 | z = getlogin (); | |
556 | if (z == NULL) | |
557 | q = NULL; | |
558 | else | |
559 | { | |
560 | q = getpwnam (z); | |
561 | if (q != NULL) | |
562 | z = q->pw_name; | |
563 | } | |
564 | if (q == NULL || q->pw_uid != getuid ()) | |
565 | { | |
566 | q = getpwuid (getuid ()); | |
567 | if (q == NULL) | |
568 | ulog (LOG_FATAL, "Can't get login name"); | |
569 | z = q->pw_name; | |
570 | } | |
571 | zSlogin = xstrdup (z); | |
572 | ||
573 | if (fdaemon) | |
574 | { | |
575 | /* Set our uid to our effective uid. There is no point in | |
576 | remembering who originally ran the program. This won't work | |
577 | on System V, but there's nothing to be done about that and it | |
578 | doesn't make all that much difference. */ | |
579 | (void) setuid (geteuid ()); | |
580 | (void) setgid (getegid ()); | |
581 | } | |
582 | ||
583 | if (fgetcwd) | |
584 | { | |
585 | const char *zenv; | |
586 | struct stat senv, sdot; | |
587 | ||
588 | /* Get the current working directory. We have to get it now, | |
589 | since we're about to do a chdir. We use PWD if it's defined | |
590 | and if it really names the working directory, since if it's | |
591 | not the same as whatever getcwd returns it's probably more | |
592 | appropriate. */ | |
593 | zenv = getenv ("PWD"); | |
594 | if (zenv != NULL | |
595 | && stat (zenv, &senv) == 0 | |
596 | && stat (".", &sdot) == 0 | |
597 | && senv.st_ino == sdot.st_ino | |
598 | && senv.st_dev == sdot.st_dev) | |
599 | zScwd = xstrdup (zenv); | |
600 | else | |
601 | { | |
602 | ||
603 | #if HAVE_GETCWD || ! HAVE_GETWD | |
604 | { | |
605 | int c; | |
606 | ||
607 | c = 128; | |
608 | while (TRUE) | |
609 | { | |
610 | zScwd = (char *) xmalloc (c); | |
611 | if (getcwd (zScwd, c) != NULL) | |
612 | break; | |
613 | if (errno != ERANGE) | |
614 | ulog (LOG_FATAL, "getcwd: %s", strerror (errno)); | |
615 | xfree ((pointer) zScwd); | |
616 | c <<= 1; | |
617 | } | |
618 | } | |
619 | #endif /* HAVE_GETCWD */ | |
620 | ||
621 | #if HAVE_GETWD | |
622 | zScwd = (char *) xmalloc (MAXPATHLEN); | |
623 | /* The getwd function puts in an error message in the | |
624 | buffer, rather than setting errno. */ | |
625 | if (getwd (zScwd) == NULL) | |
626 | ulog (LOG_FATAL, "getwd: %s", zScwd); | |
627 | #endif /* HAVE_GETWD */ | |
628 | ||
629 | zScwd = (char *) xrealloc ((pointer) zScwd, strlen (zScwd) + 1); | |
630 | } | |
631 | } | |
632 | ||
633 | /* Connect to the spool directory, and create it if is doesn't | |
634 | exist. */ | |
635 | if (chdir (zSpooldir) < 0) | |
636 | { | |
637 | if (errno != ENOENT) | |
638 | ulog (LOG_FATAL, "chdir (%s): %s", zSpooldir, | |
639 | strerror (errno)); | |
640 | usmake_spool_dir (); | |
641 | } | |
642 | } | |
643 | ||
644 | /* Exit the program. */ | |
645 | ||
646 | void | |
647 | usysdep_exit (fsuccess) | |
648 | boolean fsuccess; | |
649 | { | |
650 | exit (fsuccess ? EXIT_SUCCESS : EXIT_FAILURE); | |
651 | } | |
652 | ||
653 | /* This is called when a non-standard configuration file is used, to | |
654 | make sure the program doesn't hand out privileged file access. | |
655 | This means that to test non-standard configuration files, you | |
656 | should be logged in as uucp. This is called before | |
657 | usysdep_initialize. It ensures that someone can't simply use an | |
658 | alternate configuration file to steal UUCP transfers from other | |
659 | systems. This will still permit people to set up their own | |
660 | configuration file and pretend to be whatever system they choose. | |
661 | The only real security is to use a high level of protection on the | |
662 | modem ports. */ | |
663 | ||
664 | /*ARGSUSED*/ | |
665 | boolean fsysdep_other_config (z) | |
666 | const char *z; | |
667 | { | |
668 | (void) setuid (getuid ()); | |
669 | (void) setgid (getgid ()); | |
670 | return TRUE; | |
671 | } | |
672 | \f | |
673 | /* Detach from the controlling terminal. This is called by uucico if | |
674 | it is calling out to another system, so that it can receive SIGHUP | |
675 | signals from the port it calls out on. It is also called by uucico | |
676 | just before it starts uuxqt, so that uuxqt is completely | |
677 | independent of the terminal. */ | |
678 | ||
679 | #ifdef TIOCNOTTY | |
680 | #define HAVE_TIOCNOTTY 1 | |
681 | #else | |
682 | #define HAVE_TIOCNOTTY 0 | |
683 | #endif | |
684 | ||
685 | void | |
686 | usysdep_detach () | |
687 | { | |
688 | int o; | |
689 | ||
690 | #if ! HAVE_BSD_PGRP || ! HAVE_TIOCNOTTY | |
691 | ||
692 | pid_t igrp; | |
693 | ||
694 | /* First make sure we are not a process group leader. If we have | |
695 | TIOCNOTTY, this doesn't matter, since TIOCNOTTY sets our process | |
696 | group to 0 anyhow. */ | |
697 | ||
698 | #if HAVE_BSD_PGRP | |
699 | igrp = getpgrp (0); | |
700 | #else | |
701 | igrp = getpgrp (); | |
702 | #endif | |
703 | ||
704 | if (igrp == getpid ()) | |
705 | { | |
706 | boolean fignored; | |
707 | pid_t ipid; | |
708 | ||
709 | /* Ignore SIGHUP, since our process group leader is about to | |
710 | die. */ | |
711 | usset_signal (SIGHUP, SIG_IGN, FALSE, &fignored); | |
712 | ||
713 | ipid = isfork (); | |
714 | if (ipid < 0) | |
715 | ulog (LOG_FATAL, "fork: %s", strerror (errno)); | |
716 | ||
717 | if (ipid != 0) | |
718 | _exit (EXIT_SUCCESS); | |
719 | ||
720 | /* We'll always wind up as a child of process number 1, right? | |
721 | Right? We have to wait for our parent to die before | |
722 | reenabling SIGHUP. */ | |
723 | while (getppid () != 1) | |
724 | sleep (1); | |
725 | ||
726 | /* Restore SIGHUP catcher if it wasn't being ignored. */ | |
727 | if (! fignored) | |
728 | usset_signal (SIGHUP, ussignal, TRUE, (boolean *) NULL); | |
729 | } | |
730 | ||
731 | #endif /* ! HAVE_BSD_PGRP || ! HAVE_TIOCNOTTY */ | |
732 | ||
733 | /* Close all open files. */ | |
734 | ||
735 | ulog_close (); | |
736 | ||
737 | for (o = 0; o < cSdescriptors; o++) | |
738 | (void) close (o); | |
739 | ||
740 | /* Reopen stdin, stdout and stderr. */ | |
741 | ||
742 | if (open ("/dev/null", O_RDONLY) != 0 | |
743 | || open ("/dev/null", O_WRONLY) != 1 | |
744 | || open ("/dev/null", O_WRONLY) != 2) | |
745 | ulog (LOG_FATAL, "open (/dev/null): %s", strerror (errno)); | |
746 | ||
747 | #if HAVE_BSD_PGRP | |
748 | ||
749 | #if HAVE_TIOCNOTTY | |
750 | /* Lose our controlling terminal. */ | |
751 | ||
752 | #ifndef O_NDELAY | |
753 | #define O_NDELAY FNDELAY | |
754 | #endif | |
755 | ||
756 | o = open ("/dev/tty", O_RDWR | O_NDELAY, 0); | |
757 | if (o >= 0) | |
758 | { | |
759 | (void) ioctl (o, TIOCNOTTY, (char *) NULL); | |
760 | (void) close (o); | |
761 | } | |
762 | #endif /* HAVE_TIOCNOTTY */ | |
763 | ||
764 | /* Make sure our process group ID is set to 0. On BSD TIOCNOTTY | |
765 | should already have set it 0, so this will do no harm. On System | |
766 | V we presumably did not execute the TIOCNOTTY call, but the | |
767 | System V setpgrp will detach the controlling terminal anyhow. | |
768 | This lets us use the same code on both BSD and System V, provided | |
769 | it compiles correctly, which life easier for the configure | |
770 | script. We don't output an error if we got EPERM because some | |
771 | BSD variants don't permit this usage of setpgrp (which means they | |
772 | don't provide any way to pick up a new controlling terminal). */ | |
773 | ||
774 | if (setpgrp (0, 0) < 0) | |
775 | { | |
776 | if (errno != EPERM) | |
777 | ulog (LOG_ERROR, "setpgrp: %s", strerror (errno)); | |
778 | } | |
779 | ||
780 | #else /* ! HAVE_BSD_PGRP */ | |
781 | ||
782 | #if HAVE_SETSID | |
783 | ||
784 | /* Under POSIX the setsid call creates a new session for which we | |
785 | are the process group leader. It also detaches us from our | |
786 | controlling terminal. I'm using the BSD setpgrp call first | |
787 | because they should be equivalent for my purposes, but it turns | |
788 | out that on Ultrix 4.0 setsid prevents us from ever acquiring | |
789 | another controlling terminal (it does not change our process | |
790 | group, and Ultrix 4.0 prevents us from setting our process group | |
791 | to 0). */ | |
792 | ||
793 | if (setsid () < 0) | |
794 | ulog (LOG_ERROR, "setsid: %s", strerror (errno)); | |
795 | ||
796 | #else /* ! HAVE_SETSID */ | |
797 | ||
798 | #if HAVE_SETPGRP | |
799 | ||
800 | /* Now we assume we have the System V setpgrp, which takes no | |
801 | arguments, and we couldn't compile the HAVE_BSD_PGRP code above | |
802 | because there was a prototype somewhere in scope. On System V | |
803 | setpgrp makes us the leader of a new process group and also | |
804 | detaches the controlling terminal. */ | |
805 | ||
806 | if (setpgrp () < 0) | |
807 | ulog (LOG_ERROR, "setpgrp: %s", strerror (errno)); | |
808 | ||
809 | #else /* ! HAVE_SETPGRP */ | |
810 | ||
811 | #error Must detach from controlling terminal | |
812 | ||
813 | #endif /* HAVE_SETPGRP */ | |
814 | #endif /* ! HAVE_SETSID */ | |
815 | #endif /* ! HAVE_BSD_PGRP */ | |
816 | ||
817 | /* At this point we have completely detached from our controlling | |
818 | terminal. The next terminal device we open will probably become | |
819 | our controlling terminal. */ | |
820 | } | |
821 | \f | |
822 | /* Get the node name to use if it was not specified in the configuration | |
823 | file. */ | |
824 | ||
825 | const char * | |
826 | zsysdep_local_name () | |
827 | { | |
828 | #if HAVE_GETHOSTNAME | |
829 | char ab[256]; | |
830 | ||
831 | if (gethostname (ab, sizeof ab) < 0) | |
832 | { | |
833 | ulog (LOG_ERROR, "gethostname: %s", strerror (errno)); | |
834 | return NULL; | |
835 | } | |
836 | ab[sizeof ab - 1] = '\0'; | |
837 | ab[strcspn (ab, ".")] = '\0'; | |
838 | return xstrdup (ab); | |
839 | #else /* ! HAVE_GETHOSTNAME */ | |
840 | #if HAVE_UNAME | |
841 | struct utsname s; | |
842 | ||
843 | if (uname (&s) < 0) | |
844 | { | |
845 | ulog (LOG_ERROR, "uname: %s", strerror (errno)); | |
846 | return NULL; | |
847 | } | |
848 | return xstrdup (s.nodename); | |
849 | #else /* ! HAVE_UNAME */ | |
850 | return NULL; | |
851 | #endif /* ! HAVE_UNAME */ | |
852 | #endif /* ! HAVE_GETHOSTNAME */ | |
853 | } | |
854 | \f | |
855 | /* Get the login name. We actually get the login name in | |
856 | usysdep_initialize, because after that we will lost the real uid. */ | |
857 | ||
858 | const char * | |
859 | zsysdep_login_name () | |
860 | { | |
861 | return zSlogin; | |
862 | } | |
863 | ||
864 | /* Get the port name of standard input. I assume that Unix systems | |
865 | generally support ttyname. If they don't, this function can just | |
866 | return NULL. It uses getsockname to see whether standard input is | |
867 | a TCP connection. */ | |
868 | ||
869 | const char * | |
870 | zsysdep_port_name (ftcp_port) | |
871 | boolean *ftcp_port; | |
872 | { | |
873 | const char *z; | |
874 | ||
875 | *ftcp_port = FALSE; | |
876 | ||
877 | #if HAVE_TCP | |
878 | { | |
879 | int clen; | |
880 | ||
881 | clen = 0; | |
882 | if (getsockname (0, (struct sockaddr *) NULL, &clen) == 0) | |
883 | *ftcp_port = TRUE; | |
884 | } | |
885 | #endif /* HAVE_TCP */ | |
886 | ||
887 | z = ttyname (0); | |
888 | if (z == NULL) | |
889 | return NULL; | |
890 | if (strncmp (z, "/dev/", 5) == 0) | |
891 | return z + 5; | |
892 | else | |
893 | return z; | |
894 | } | |
895 | \f | |
896 | /* Signal handling routines. When we catch a signal, we want to set | |
897 | the appropriate elements of afSignal and afLog_signal to TRUE. If | |
898 | we are on a system which restarts system calls, we may also want to | |
899 | longjmp out. On a system which does not restart system calls, | |
900 | these signal handling routines are well-defined by ANSI C. */ | |
901 | ||
902 | #if HAVE_RESTARTABLE_SYSCALLS | |
903 | volatile sig_atomic_t fSjmp; | |
904 | volatile jmp_buf sSjmp_buf; | |
905 | #endif /* HAVE_RESTARTABLE_SYSCALLS */ | |
906 | ||
907 | /* The SVR3 sigset function can be called just like signal, unless | |
908 | system calls are restarted which is extremely unlikely; we prevent | |
909 | this case in sysh.unx. */ | |
910 | #if HAVE_SIGSET && ! HAVE_SIGACTION && ! HAVE_SIGVEC | |
911 | #define signal sigset | |
912 | #endif | |
913 | ||
914 | /* Catch a signal. Reinstall the signal handler if necessary, set the | |
915 | appropriate variables, and do a longjmp if necessary. */ | |
916 | ||
917 | SIGtype | |
918 | ussignal (isig) | |
919 | int isig; | |
920 | { | |
921 | int iindex; | |
922 | ||
923 | #if ! HAVE_SIGACTION && ! HAVE_SIGVEC && ! HAVE_SIGSET | |
924 | (void) signal (isig, ussignal); | |
925 | #endif | |
926 | ||
927 | switch (isig) | |
928 | { | |
929 | default: iindex = INDEXSIG_SIGHUP; break; | |
930 | #ifdef SIGINT | |
931 | case SIGINT: iindex = INDEXSIG_SIGINT; break; | |
932 | #endif | |
933 | #ifdef SIGQUIT | |
934 | case SIGQUIT: iindex = INDEXSIG_SIGQUIT; break; | |
935 | #endif | |
936 | #ifdef SIGTERM | |
937 | case SIGTERM: iindex = INDEXSIG_SIGTERM; break; | |
938 | #endif | |
939 | #ifdef SIGPIPE | |
940 | case SIGPIPE: iindex = INDEXSIG_SIGPIPE; break; | |
941 | #endif | |
942 | } | |
943 | ||
944 | afSignal[iindex] = TRUE; | |
945 | afLog_signal[iindex] = TRUE; | |
946 | ||
947 | #if HAVE_RESTARTABLE_SYSCALLS | |
948 | if (fSjmp) | |
949 | longjmp (sSjmp_buf, 1); | |
950 | #endif /* HAVE_RESTARTABLE_SYSCALLS */ | |
951 | } | |
952 | ||
953 | /* Prepare to catch a signal. This is basically the ANSI C routine | |
954 | signal, but it uses sigaction or sigvec instead if they are | |
955 | available. If fforce is FALSE, we do not set the signal if it is | |
956 | currently being ignored. If pfignored is not NULL and fforce is | |
957 | FALSE, then *pfignored will be set to TRUE if the signal was | |
958 | previously being ignored (if fforce is TRUE the value of *pfignored | |
959 | is meaningless). If we can't change the signal handler we give a | |
960 | fatal error. */ | |
961 | ||
962 | void | |
963 | usset_signal (isig, pfn, fforce, pfignored) | |
964 | int isig; | |
965 | SIGtype (*pfn) P((int)); | |
966 | boolean fforce; | |
967 | boolean *pfignored; | |
968 | { | |
969 | #if HAVE_SIGACTION | |
970 | ||
971 | struct sigaction s; | |
972 | ||
973 | if (! fforce) | |
974 | { | |
975 | sigemptyset (&s.sa_mask); | |
976 | if (sigaction (isig, (struct sigaction *) NULL, &s) != 0) | |
977 | ulog (LOG_FATAL, "sigaction (%d): %s", isig, strerror (errno)); | |
978 | ||
979 | if (s.sa_handler == SIG_IGN) | |
980 | { | |
981 | if (pfignored != NULL) | |
982 | *pfignored = TRUE; | |
983 | return; | |
984 | } | |
985 | ||
986 | if (pfignored != NULL) | |
987 | *pfignored = FALSE; | |
988 | } | |
989 | ||
990 | s.sa_handler = pfn; | |
991 | sigemptyset (&s.sa_mask); | |
992 | s.sa_flags = 0; | |
993 | ||
994 | if (sigaction (isig, &s, (struct sigaction *) NULL) != 0) | |
995 | ulog (LOG_FATAL, "sigaction (%d): %s", isig, strerror (errno)); | |
996 | ||
997 | #else /* ! HAVE_SIGACTION */ | |
998 | #if HAVE_SIGVEC | |
999 | ||
1000 | struct sigvec s; | |
1001 | ||
1002 | if (! fforce) | |
1003 | { | |
1004 | if (sigvec (isig, (struct sigvec *) NULL, &s) != 0) | |
1005 | ulog (LOG_FATAL, "sigvec (%d): %s", isig, strerror (errno)); | |
1006 | ||
1007 | if (s.sv_handler == SIG_IGN) | |
1008 | { | |
1009 | if (pfignored != NULL) | |
1010 | *pfignored = TRUE; | |
1011 | return; | |
1012 | } | |
1013 | ||
1014 | if (pfignored != NULL) | |
1015 | *pfignored = FALSE; | |
1016 | } | |
1017 | ||
1018 | s.sv_handler = pfn; | |
1019 | s.sv_mask = 0; | |
1020 | #ifdef SV_INTERRUPT | |
1021 | s.sv_flags = SV_INTERRUPT; | |
1022 | #else | |
1023 | s.sv_flags = 0; | |
1024 | #endif | |
1025 | ||
1026 | if (sigvec (isig, &s, (struct sigvec *) NULL) != 0) | |
1027 | ulog (LOG_FATAL, "sigvec (%d): %s", isig, strerror (errno)); | |
1028 | ||
1029 | #else /* ! HAVE_SIGVEC */ | |
1030 | ||
1031 | if (! fforce) | |
1032 | { | |
1033 | if (signal (isig, SIG_IGN) == SIG_IGN) | |
1034 | { | |
1035 | if (pfignored != NULL) | |
1036 | *pfignored = TRUE; | |
1037 | return; | |
1038 | } | |
1039 | ||
1040 | if (pfignored != NULL) | |
1041 | *pfignored = FALSE; | |
1042 | } | |
1043 | ||
1044 | (void) signal (isig, pfn); | |
1045 | ||
1046 | #endif /* ! HAVE_SIGVEC */ | |
1047 | #endif /* ! HAVE_SIGACTION */ | |
1048 | } | |
1049 | ||
1050 | /* The routine called by the system independent code, which always | |
1051 | uses the same signal handler. */ | |
1052 | ||
1053 | void | |
1054 | usysdep_signal (isig) | |
1055 | int isig; | |
1056 | { | |
1057 | usset_signal (isig, ussignal, FALSE, (boolean *) NULL); | |
1058 | } | |
1059 | \f | |
1060 | /* Get the time in seconds since the epoch, with optional | |
1061 | microseconds. We use usysdep_process_time to get the microseconds | |
1062 | if it will work (it won't is it uses times, since that returns a | |
1063 | time based only on the process). */ | |
1064 | ||
1065 | long | |
1066 | isysdep_time (pimicros) | |
1067 | long *pimicros; | |
1068 | { | |
1069 | #if HAVE_GETTIMEOFDAY || HAVE_FTIME | |
1070 | return isysdep_process_time (pimicros); | |
1071 | #else | |
1072 | if (pimicros != NULL) | |
1073 | *pimicros = 0; | |
1074 | return time ((time_t *) NULL); | |
1075 | #endif | |
1076 | } | |
1077 | ||
1078 | /* Get the time in seconds and microseconds; this need only work | |
1079 | within the process when called from the system independent code. | |
1080 | It is also called by isysdep_time, above. */ | |
1081 | ||
1082 | long | |
1083 | isysdep_process_time (pimicros) | |
1084 | long *pimicros; | |
1085 | { | |
1086 | #if HAVE_GETTIMEOFDAY | |
1087 | struct timeval stime; | |
1088 | struct timezone stz; | |
1089 | ||
1090 | (void) gettimeofday (&stime, &stz); | |
1091 | if (pimicros != NULL) | |
1092 | *pimicros = stime.tv_usec; | |
1093 | return stime.tv_sec; | |
1094 | #endif /* HAVE_GETTIMEOFDAY */ | |
1095 | ||
1096 | #if HAVE_FTIME | |
1097 | struct timeb stime; | |
1098 | ||
1099 | (void) ftime (&stime); | |
1100 | if (pimicros != NULL) | |
1101 | *pimicros = stime.millitm * 1000; | |
1102 | return stime.time; | |
1103 | #endif /* HAVE_FTIME */ | |
1104 | ||
1105 | #if HAVE_TIMES | |
1106 | struct tms s; | |
1107 | long i; | |
1108 | static int itick; | |
1109 | ||
1110 | if (itick == 0) | |
1111 | { | |
1112 | #if TIMES_TICK != 0 | |
1113 | itick = TIMES_TICK; | |
1114 | #else /* TIMES_TICK == 0 */ | |
1115 | const char *z; | |
1116 | ||
1117 | z = getenv ("HZ"); | |
1118 | if (z != NULL) | |
1119 | itick = atoi (z); | |
1120 | ||
1121 | /* If we really couldn't get anything, just use 60. */ | |
1122 | if (itick == 0) | |
1123 | itick = 60; | |
1124 | #endif /* TIMES_TICK == 0 */ | |
1125 | } | |
1126 | ||
1127 | i = (long) times (&s); | |
1128 | if (pimicros != NULL) | |
1129 | *pimicros = (i % (long) itick) * ((long) 1000000 / (long) itick); | |
1130 | return i / (long) itick; | |
1131 | #endif /* HAVE_TIMES */ | |
1132 | ||
1133 | #if ! HAVE_GETTIMEOFDAY && ! HAVE_FTIME && ! HAVE_TIMES | |
1134 | if (pimicros != NULL) | |
1135 | *pimicros = 0; | |
1136 | return time ((time_t *) NULL); | |
1137 | #endif /* ! HAVE_GETTIMEOFDAY && ! HAVE_FTIME && ! HAVE_TIMES */ | |
1138 | } | |
1139 | ||
1140 | /* Fill in a struct tm. */ | |
1141 | ||
1142 | void | |
1143 | usysdep_localtime (itime, q) | |
1144 | long itime; | |
1145 | struct tm *q; | |
1146 | { | |
1147 | time_t i; | |
1148 | ||
1149 | i = (time_t) itime; | |
1150 | memcpy (q, localtime (&i), sizeof (struct tm)); | |
1151 | } | |
1152 | ||
1153 | /* Sleep for a number of seconds. */ | |
1154 | ||
1155 | void | |
1156 | usysdep_sleep (c) | |
1157 | int c; | |
1158 | { | |
1159 | sleep (c); | |
1160 | } | |
1161 | \f | |
1162 | /* Check whether a file exists. */ | |
1163 | ||
1164 | boolean | |
1165 | fsysdep_file_exists (zfile) | |
1166 | const char *zfile; | |
1167 | { | |
1168 | struct stat s; | |
1169 | ||
1170 | return stat (zfile, &s) == 0; | |
1171 | } | |
1172 | \f | |
1173 | /* Open a stdio file with appropriate permissions. */ | |
1174 | ||
1175 | FILE * | |
1176 | esysdep_fopen (zfile, fpublic, fappend, fmkdirs) | |
1177 | const char *zfile; | |
1178 | boolean fpublic; | |
1179 | boolean fappend; | |
1180 | boolean fmkdirs; | |
1181 | { | |
1182 | int imode; | |
1183 | int o; | |
1184 | FILE *e; | |
1185 | ||
1186 | if (fpublic) | |
1187 | imode = IPUBLIC_FILE_MODE; | |
1188 | else | |
1189 | imode = IPRIVATE_FILE_MODE; | |
1190 | ||
1191 | if (! fappend) | |
1192 | o = creat (zfile, imode); | |
1193 | else | |
1194 | { | |
1195 | #ifdef O_CREAT | |
1196 | o = open (zfile, O_WRONLY | O_APPEND | O_CREAT | O_NOCTTY, imode); | |
1197 | #else | |
1198 | o = open (zfile, O_WRONLY | O_NOCTTY); | |
1199 | if (o < 0 && errno == ENOENT) | |
1200 | o = creat (zfile, imode); | |
1201 | #endif /* ! defined (O_CREAT) */ | |
1202 | } | |
1203 | ||
1204 | if (o < 0) | |
1205 | { | |
1206 | if (errno == ENOENT && fmkdirs) | |
1207 | { | |
1208 | if (! fsysdep_make_dirs (zfile, fpublic)) | |
1209 | return NULL; | |
1210 | if (! fappend) | |
1211 | o = creat (zfile, imode); | |
1212 | else | |
1213 | { | |
1214 | #ifdef O_CREAT | |
1215 | o = open (zfile, O_WRONLY | O_APPEND | O_CREAT, imode); | |
1216 | #else | |
1217 | o = creat (zfile, imode); | |
1218 | #endif | |
1219 | } | |
1220 | } | |
1221 | if (o < 0) | |
1222 | { | |
1223 | ulog (LOG_ERROR, "open (%s): %s", zfile, strerror (errno)); | |
1224 | return NULL; | |
1225 | } | |
1226 | } | |
1227 | ||
1228 | #ifndef O_CREAT | |
1229 | #ifdef O_APPEND | |
1230 | if (fappend) | |
1231 | { | |
1232 | if (fcntl (o, F_SETFL, O_APPEND) != 0) | |
1233 | { | |
1234 | ulog (LOG_ERROR, "fcntl (%s, O_APPEND): %s", zfile, | |
1235 | strerror (errno)); | |
1236 | (void) close (o); | |
1237 | return NULL; | |
1238 | } | |
1239 | } | |
1240 | #endif /* defined (O_APPEND) */ | |
1241 | #endif /* ! defined (O_CREAT) */ | |
1242 | ||
1243 | if (fappend) | |
1244 | e = fdopen (o, (char *) "a"); | |
1245 | else | |
1246 | e = fdopen (o, (char *) "w"); | |
1247 | ||
1248 | if (e == NULL) | |
1249 | { | |
1250 | ulog (LOG_ERROR, "fdopen: %s", strerror (errno)); | |
1251 | (void) close (o); | |
1252 | } | |
1253 | ||
1254 | return e; | |
1255 | } | |
1256 | \f | |
1257 | /* See whether a directory exists. */ | |
1258 | ||
1259 | boolean | |
1260 | fsdirectory_exists (z) | |
1261 | const char *z; | |
1262 | { | |
1263 | struct stat s; | |
1264 | ||
1265 | if (stat (z, &s) < 0) | |
1266 | return FALSE; | |
1267 | return S_ISDIR (s.st_mode); | |
1268 | } | |
1269 | ||
1270 | /* Create any directories needed for a file name. */ | |
1271 | ||
1272 | boolean | |
1273 | fsysdep_make_dirs (zfile, fpublic) | |
1274 | const char *zfile; | |
1275 | boolean fpublic; | |
1276 | { | |
1277 | char *zcopy, *z; | |
1278 | int imode; | |
1279 | ||
1280 | zcopy = (char *) alloca (strlen (zfile) + 1); | |
1281 | strcpy (zcopy, zfile); | |
1282 | ||
1283 | if (fpublic) | |
1284 | imode = IPUBLIC_DIRECTORY_MODE; | |
1285 | else | |
1286 | imode = IDIRECTORY_MODE; | |
1287 | ||
1288 | for (z = zcopy; *z != '\0'; z++) | |
1289 | { | |
1290 | if (*z == '/' && z != zcopy) | |
1291 | { | |
1292 | *z = '\0'; | |
1293 | if (! fsdirectory_exists (zcopy)) | |
1294 | { | |
1295 | if (mkdir (zcopy, imode) != 0) | |
1296 | { | |
1297 | ulog (LOG_ERROR, "mkdir (%s): %s", zcopy, | |
1298 | strerror (errno)); | |
1299 | return FALSE; | |
1300 | } | |
1301 | } | |
1302 | *z = '/'; | |
1303 | } | |
1304 | } | |
1305 | ||
1306 | return TRUE; | |
1307 | } | |
1308 | \f | |
1309 | /* Tilde expand a file or directory name. */ | |
1310 | ||
1311 | /*ARGSUSED*/ | |
1312 | const char * | |
1313 | zstilde_expand (qsys, zfile) | |
1314 | const struct ssysteminfo *qsys; | |
1315 | const char *zfile; | |
1316 | { | |
1317 | static int calc; | |
1318 | static char *zalc; | |
1319 | const char *zdir; | |
1320 | int clen; | |
1321 | ||
1322 | if (zfile[0] != '~') | |
1323 | return zfile; | |
1324 | else if (zfile[1] == '\0' || zfile[1] == '/') | |
1325 | { | |
1326 | const char *zpub; | |
1327 | ||
1328 | if (qsys->zpubdir == NULL) | |
1329 | zpub = zPubdir; | |
1330 | else | |
1331 | zpub = qsys->zpubdir; | |
1332 | if (zfile[1] == '\0') | |
1333 | return zpub; | |
1334 | else | |
1335 | { | |
1336 | zdir = zpub; | |
1337 | zfile += 2; | |
1338 | } | |
1339 | } | |
1340 | else | |
1341 | { | |
1342 | int cuserlen; | |
1343 | char *zcopy; | |
1344 | struct passwd *q; | |
1345 | ||
1346 | ++zfile; | |
1347 | cuserlen = strcspn (zfile, "/"); | |
1348 | zcopy = (char *) alloca (cuserlen + 1); | |
1349 | strncpy (zcopy, zfile, cuserlen); | |
1350 | zcopy[cuserlen] = '\0'; | |
1351 | ||
1352 | q = getpwnam (zcopy); | |
1353 | if (q == NULL) | |
1354 | { | |
1355 | ulog (LOG_ERROR, "User %s not found", zcopy); | |
1356 | return NULL; | |
1357 | } | |
1358 | ||
1359 | if (zfile[cuserlen] == '\0') | |
1360 | return q->pw_dir; | |
1361 | else | |
1362 | { | |
1363 | zdir = q->pw_dir; | |
1364 | zfile += cuserlen + 1; | |
1365 | } | |
1366 | } | |
1367 | ||
1368 | clen = strlen (zdir) + strlen (zfile) + 2; | |
1369 | if (clen > calc) | |
1370 | { | |
1371 | zalc = (char *) xrealloc ((pointer) zalc, clen); | |
1372 | calc = clen; | |
1373 | } | |
1374 | ||
1375 | sprintf (zalc, "%s/%s", zdir, zfile); | |
1376 | return zalc; | |
1377 | } | |
1378 | \f | |
1379 | /* Do access(2) on a stat structure, except that the user name is | |
1380 | provided. If the user name in zuser is NULL, require the file to | |
1381 | be accessible to the world. Return TRUE if access is permitted, | |
1382 | FALSE otherwise. This does not log an error message. */ | |
1383 | ||
1384 | boolean | |
1385 | fsuser_access (q, imode, zuser) | |
1386 | const struct stat *q; | |
1387 | int imode; | |
1388 | const char *zuser; | |
1389 | { | |
1390 | static char *zuser_hold; | |
1391 | static uid_t iuid_hold; | |
1392 | static gid_t igid_hold; | |
1393 | static int cgroups_hold; | |
1394 | static gid_t *paigroups_hold; | |
1395 | int ir, iw, ix, iand; | |
1396 | ||
1397 | if (imode == F_OK) | |
1398 | return TRUE; | |
1399 | ||
1400 | if (zuser != NULL) | |
1401 | { | |
1402 | /* We keep static variables around for the last user we did, to | |
1403 | avoid looking up a user multiple times. */ | |
1404 | if (zuser_hold == NULL || strcmp (zuser_hold, zuser) != 0) | |
1405 | { | |
1406 | struct passwd *qpwd; | |
1407 | ||
1408 | if (zuser_hold != NULL) | |
1409 | { | |
1410 | xfree ((pointer) zuser_hold); | |
1411 | zuser_hold = NULL; | |
1412 | cgroups_hold = 0; | |
1413 | xfree ((pointer) paigroups_hold); | |
1414 | paigroups_hold = NULL; | |
1415 | } | |
1416 | ||
1417 | qpwd = getpwnam ((char *) zuser); | |
1418 | if (qpwd == NULL) | |
1419 | { | |
1420 | /* Check this as a remote request. */ | |
1421 | zuser = NULL; | |
1422 | } | |
1423 | else | |
1424 | { | |
1425 | #if HAVE_GETGRENT | |
1426 | struct group *qg; | |
1427 | #endif | |
1428 | ||
1429 | zuser_hold = xstrdup (zuser); | |
1430 | ||
1431 | iuid_hold = qpwd->pw_uid; | |
1432 | igid_hold = qpwd->pw_gid; | |
1433 | ||
1434 | #if HAVE_GETGRENT | |
1435 | /* Get the list of groups for this user. This is | |
1436 | definitely more appropriate for BSD than for System | |
1437 | V. It may just be a waste of time, and perhaps it | |
1438 | should be configurable. */ | |
1439 | setgrent (); | |
1440 | while ((qg = getgrent ()) != NULL) | |
1441 | { | |
1442 | const char **pz; | |
1443 | ||
1444 | if (qg->gr_gid == igid_hold) | |
1445 | continue; | |
1446 | for (pz = (const char **) qg->gr_mem; *pz != NULL; pz++) | |
1447 | { | |
1448 | if ((*pz)[0] == *zuser | |
1449 | && strcmp (*pz, zuser) == 0) | |
1450 | { | |
1451 | paigroups_hold = ((gid_t *) | |
1452 | (xrealloc | |
1453 | ((pointer) paigroups_hold, | |
1454 | ((cgroups_hold + 1) | |
1455 | * sizeof (gid_t))))); | |
1456 | paigroups_hold[cgroups_hold] = qg->gr_gid; | |
1457 | ++cgroups_hold; | |
1458 | break; | |
1459 | } | |
1460 | } | |
1461 | } | |
1462 | endgrent (); | |
1463 | #endif | |
1464 | } | |
1465 | } | |
1466 | } | |
1467 | ||
1468 | ||
1469 | /* Now do the actual access check. */ | |
1470 | ||
1471 | if (zuser != NULL) | |
1472 | { | |
1473 | /* The superuser can do anything. */ | |
1474 | if (iuid_hold == 0) | |
1475 | return TRUE; | |
1476 | ||
1477 | /* If this is the uid we're running under, there's no point to | |
1478 | checking access further, because when we actually try the | |
1479 | operation the system will do the checking for us. */ | |
1480 | if (iuid_hold == geteuid ()) | |
1481 | return TRUE; | |
1482 | } | |
1483 | ||
1484 | ir = S_IROTH; | |
1485 | iw = S_IWOTH; | |
1486 | ix = S_IXOTH; | |
1487 | ||
1488 | if (zuser != NULL) | |
1489 | { | |
1490 | if (iuid_hold == q->st_uid) | |
1491 | { | |
1492 | ir = S_IRUSR; | |
1493 | iw = S_IWUSR; | |
1494 | ix = S_IXUSR; | |
1495 | } | |
1496 | else | |
1497 | { | |
1498 | boolean fgroup; | |
1499 | ||
1500 | fgroup = FALSE; | |
1501 | if (igid_hold == q->st_gid) | |
1502 | fgroup = TRUE; | |
1503 | else | |
1504 | { | |
1505 | int i; | |
1506 | ||
1507 | for (i = 0; i < cgroups_hold; i++) | |
1508 | { | |
1509 | if (paigroups_hold[i] == q->st_gid) | |
1510 | { | |
1511 | fgroup = TRUE; | |
1512 | break; | |
1513 | } | |
1514 | } | |
1515 | } | |
1516 | ||
1517 | if (fgroup) | |
1518 | { | |
1519 | ir = S_IRGRP; | |
1520 | iw = S_IWGRP; | |
1521 | ix = S_IXGRP; | |
1522 | } | |
1523 | } | |
1524 | } | |
1525 | ||
1526 | iand = 0; | |
1527 | if ((imode & R_OK) != 0) | |
1528 | iand |= ir; | |
1529 | if ((imode & W_OK) != 0) | |
1530 | iand |= iw; | |
1531 | if ((imode & X_OK) != 0) | |
1532 | iand |= ix; | |
1533 | ||
1534 | return (q->st_mode & iand) == iand; | |
1535 | } | |
1536 | ||
1537 | /* See whether a file is in a directory, and optionally check access. */ | |
1538 | ||
1539 | boolean | |
1540 | fsysdep_in_directory (qsys, zfile, zdir, fcheck, freadable, zuser) | |
1541 | const struct ssysteminfo *qsys; | |
1542 | const char *zfile; | |
1543 | const char *zdir; | |
1544 | boolean fcheck; | |
1545 | boolean freadable; | |
1546 | const char *zuser; | |
1547 | { | |
1548 | int c; | |
1549 | char *zcopy, *zslash; | |
1550 | struct stat s; | |
1551 | ||
1552 | if (*zdir == '~') | |
1553 | { | |
1554 | zdir = zstilde_expand (qsys, zdir); | |
1555 | if (zdir == NULL) | |
1556 | return FALSE; | |
1557 | } | |
1558 | c = strlen (zdir); | |
1559 | if (zdir[c - 1] == '/') | |
1560 | c--; | |
1561 | if (strncmp (zfile, zdir, c) != 0 | |
1562 | || (zfile[c] != '/' && zfile[c] != '\0')) | |
1563 | return FALSE; | |
1564 | if (strstr (zfile + c, "/../") != NULL) | |
1565 | return FALSE; | |
1566 | ||
1567 | /* If we're not checking access, get out now. */ | |
1568 | ||
1569 | if (! fcheck) | |
1570 | return TRUE; | |
1571 | ||
1572 | zcopy = (char *) alloca (strlen (zfile) + 1); | |
1573 | strcpy (zcopy, zfile); | |
1574 | ||
1575 | /* Start checking directories after zdir. Otherwise, we would | |
1576 | require that all directories down to /usr/spool/uucppublic be | |
1577 | publically searchable; they probably are but it should not be | |
1578 | requirement. */ | |
1579 | ||
1580 | zslash = zcopy + c; | |
1581 | do | |
1582 | { | |
1583 | char b; | |
1584 | struct stat shold; | |
1585 | ||
1586 | b = *zslash; | |
1587 | *zslash = '\0'; | |
1588 | ||
1589 | shold = s; | |
1590 | if (stat (zcopy, &s) != 0) | |
1591 | { | |
1592 | if (errno != ENOENT) | |
1593 | { | |
1594 | ulog (LOG_ERROR, "stat (%s): %s", zcopy, strerror (errno)); | |
1595 | return FALSE; | |
1596 | } | |
1597 | ||
1598 | /* If this is the top directory, any problems will be caught | |
1599 | later when we try to open it. */ | |
1600 | if (zslash == zcopy + c) | |
1601 | return TRUE; | |
1602 | ||
1603 | /* Go back and check the last directory for read or write | |
1604 | access. */ | |
1605 | s = shold; | |
1606 | break; | |
1607 | } | |
1608 | ||
1609 | /* If this is not a directory, get out of the loop. */ | |
1610 | if (! S_ISDIR (s.st_mode)) | |
1611 | break; | |
1612 | ||
1613 | /* Make sure the directory is searchable. */ | |
1614 | if (! fsuser_access (&s, X_OK, zuser)) | |
1615 | { | |
1616 | ulog (LOG_ERROR, "%s: %s", zcopy, strerror (EACCES)); | |
1617 | return FALSE; | |
1618 | } | |
1619 | ||
1620 | /* If we've reached the end of the string, get out. */ | |
1621 | if (b == '\0') | |
1622 | break; | |
1623 | ||
1624 | *zslash = b; | |
1625 | } | |
1626 | while ((zslash = strchr (zslash + 1, '/')) != NULL); | |
1627 | ||
1628 | /* At this point s holds a stat on the last component of the path. | |
1629 | We must check it for readability or writeability. */ | |
1630 | ||
1631 | if (! fsuser_access (&s, freadable ? R_OK : W_OK, zuser)) | |
1632 | { | |
1633 | ulog (LOG_ERROR, "%s: %s", zcopy, strerror (EACCES)); | |
1634 | return FALSE; | |
1635 | } | |
1636 | ||
1637 | return TRUE; | |
1638 | } | |
1639 | \f | |
1640 | /* Start up a new program and end the current one. We always go | |
1641 | through isspawn, and never exec directly. We don't have to worry | |
1642 | about SIGHUP because the current process is either not a process | |
1643 | group leader (uucp, uux) or it does not have a controlling terminal | |
1644 | (uucico). */ | |
1645 | ||
1646 | boolean | |
1647 | fsysdep_run (ffork, zprogram, zarg1, zarg2) | |
1648 | boolean ffork; | |
1649 | const char *zprogram; | |
1650 | const char *zarg1; | |
1651 | const char *zarg2; | |
1652 | { | |
1653 | char *zlib; | |
1654 | const char *azargs[4]; | |
1655 | int aidescs[3]; | |
1656 | pid_t ipid; | |
1657 | ||
1658 | zlib = (char *) alloca (sizeof LIBDIR + sizeof "/" + strlen (zprogram)); | |
1659 | sprintf (zlib, "%s/%s", LIBDIR, zprogram); | |
1660 | ||
1661 | azargs[0] = zlib; | |
1662 | azargs[1] = zarg1; | |
1663 | azargs[2] = zarg2; | |
1664 | azargs[3] = NULL; | |
1665 | ||
1666 | aidescs[0] = SPAWN_NULL; | |
1667 | aidescs[1] = SPAWN_NULL; | |
1668 | aidescs[2] = SPAWN_NULL; | |
1669 | ||
1670 | /* We pass fshell as TRUE, which permits uucico and uuxqt to be | |
1671 | replaced by shell scripts. */ | |
1672 | ipid = isspawn (azargs, aidescs, FALSE, FALSE, (const char *) NULL, | |
1673 | FALSE, TRUE, (const char *) NULL, | |
1674 | (const char *) NULL, (const char *) NULL); | |
1675 | if (ipid < 0) | |
1676 | { | |
1677 | ulog (LOG_ERROR, "isspawn: %s", strerror (errno)); | |
1678 | return FALSE; | |
1679 | } | |
1680 | ||
1681 | if (ffork) | |
1682 | return TRUE; | |
1683 | ||
1684 | exit (EXIT_SUCCESS); | |
1685 | } | |
1686 | \f | |
1687 | /* Mail a message to a user. */ | |
1688 | ||
1689 | boolean | |
1690 | fsysdep_mail (zto, zsubject, cstrs, paz) | |
1691 | const char *zto; | |
1692 | const char *zsubject; | |
1693 | int cstrs; | |
1694 | const char **paz; | |
1695 | { | |
1696 | const char *az[3]; | |
1697 | FILE *e; | |
1698 | pid_t ipid; | |
1699 | time_t itime; | |
1700 | int i; | |
1701 | ||
1702 | az[0] = MAIL_PROGRAM; | |
1703 | az[1] = zto; | |
1704 | az[2] = NULL; | |
1705 | ||
1706 | e = espopen (az, FALSE, &ipid); | |
1707 | if (e == NULL) | |
1708 | { | |
1709 | ulog (LOG_ERROR, "espopen (%s): %s", MAIL_PROGRAM, | |
1710 | strerror (errno)); | |
1711 | return FALSE; | |
1712 | } | |
1713 | ||
1714 | fprintf (e, "Subject: %s\n", zsubject); | |
1715 | fprintf (e, "To: %s\n", zto); | |
1716 | ||
1717 | /* We should probably put in a Date: header as well. */ | |
1718 | ||
1719 | fprintf (e, "\n"); | |
1720 | ||
1721 | (void) time (&itime); | |
1722 | /* Remember that ctime includes a \n, so this skips a line. */ | |
1723 | fprintf (e, "Message from UUCP on %s %s\n", zLocalname, | |
1724 | ctime (&itime)); | |
1725 | ||
1726 | for (i = 0; i < cstrs; i++) | |
1727 | fputs (paz[i], e); | |
1728 | ||
1729 | (void) fclose (e); | |
1730 | ||
1731 | return iswait ((unsigned long) ipid, MAIL_PROGRAM) == 0; | |
1732 | } | |
1733 | \f | |
1734 | /* Make a directory with error checking. */ | |
1735 | ||
1736 | static void | |
1737 | xmkdir (zdir) | |
1738 | const char *zdir; | |
1739 | { | |
1740 | if (mkdir ((char *) zdir, IDIRECTORY_MODE) < 0) | |
1741 | ulog (LOG_FATAL, "mkdir (%s): %s", zdir, strerror (errno)); | |
1742 | } | |
1743 | ||
1744 | /* Make the spool directory. */ | |
1745 | ||
1746 | static void | |
1747 | usmake_spool_dir () | |
1748 | { | |
1749 | xmkdir (zSpooldir); | |
1750 | ||
1751 | if (chdir (zSpooldir) < 0) | |
1752 | ulog (LOG_FATAL, "chdir (%s): %s", zSpooldir, strerror (errno)); | |
1753 | ||
1754 | #if SPOOLDIR_BSD42 | SPOOLDIR_BSD43 | |
1755 | xmkdir ("C."); | |
1756 | xmkdir ("D."); | |
1757 | #if SPOOLDIR_BSD43 | |
1758 | xmkdir ("X."); | |
1759 | #endif /* SPOOLDIR_BSD43 */ | |
1760 | { | |
1761 | char ab[sizeof "D.1234567X"]; | |
1762 | ||
1763 | sprintf (ab, "D.%.7s", zLocalname); | |
1764 | xmkdir (ab); | |
1765 | #if SPOOLDIR_BSD43 | |
1766 | strcat (ab, "X"); | |
1767 | xmkdir (ab); | |
1768 | #endif /* SPOOLDIR_BSD43 */ | |
1769 | } | |
1770 | #endif /* SPOOLDIR_BSD42 | SPOOLDIR_BSD43 */ | |
1771 | ||
1772 | #if SPOOLDIR_ULTRIX | |
1773 | xmkdir ("sys"); | |
1774 | xmkdir ("sys/DEFAULT"); | |
1775 | xmkdir ("sys/DEFAULT/C."); | |
1776 | xmkdir ("sys/DEFAULT/D."); | |
1777 | xmkdir ("sys/DEFAULT/X."); | |
1778 | { | |
1779 | char ab[sizeof "sys/DEFAULT/D.1234567X"]; | |
1780 | ||
1781 | sprintf (ab, "sys/DEFAULT/D.%.7s", zLocalname); | |
1782 | xmkdir (ab); | |
1783 | strcat (ab, "X"); | |
1784 | xmkdir (ab); | |
1785 | } | |
1786 | #endif /* SPOOLDIR_ULTRIX */ | |
1787 | ||
1788 | #if SPOOLDIR_BSD43 | SPOOLDIR_ULTRIX | SPOOLDIR_TAYLOR | |
1789 | xmkdir (".Temp"); | |
1790 | #endif /* SPOOLDIR_BSD43 | SPOOLDIR_ULTRIX | SPOOLDIR_TAYLOR */ | |
1791 | ||
1792 | xmkdir (".Status"); | |
1793 | xmkdir (".Sequence"); | |
1794 | xmkdir (XQTDIR); | |
1795 | xmkdir (PRESERVEDIR); | |
1796 | } | |
1797 | \f | |
1798 | /* Retry fork several times before giving up. */ | |
1799 | ||
1800 | pid_t | |
1801 | isfork () | |
1802 | { | |
1803 | int i; | |
1804 | pid_t iret; | |
1805 | ||
1806 | for (i = 0; i < 10; i++) | |
1807 | { | |
1808 | iret = fork (); | |
1809 | if (iret >= 0 || errno != EAGAIN) | |
1810 | return iret; | |
1811 | sleep (5); | |
1812 | } | |
1813 | ||
1814 | return iret; | |
1815 | } | |
1816 | \f | |
1817 | /* Spawn a child in a fairly secure fashion. This returns the process | |
1818 | ID of the child or -1 on error. It takes far too many arguments: | |
1819 | ||
1820 | pazargs -- arguments (element 0 is command) | |
1821 | aidescs -- file descriptors for stdin, stdout and stderr | |
1822 | fkeepuid -- TRUE if euid should be left unchanged | |
1823 | fkeepenv -- TRUE if environment should be left unmodified | |
1824 | zchdir -- directory to chdir to | |
1825 | fnosigs -- TRUE if child should ignore SIGHUP, SIGINT and SIGQUIT | |
1826 | fshell -- TRUE if should try /bin/sh if execve gets ENOEXEC | |
1827 | zpath -- value for environment variable PATH | |
1828 | zuu_machine -- value for environment variable UU_MACHINE | |
1829 | zuu_user -- value for environment variable UU_USER | |
1830 | ||
1831 | The aidescs array is three elements long. 0 is stdin, 1 is stdout | |
1832 | and 2 is stderr. The array may contain either file descriptor | |
1833 | numbers to dup appropriately, or one of the following: | |
1834 | ||
1835 | SPAWN_NULL -- set descriptor to /dev/null | |
1836 | SPAWN_READ_PIPE -- set aidescs element to pipe for parent to read | |
1837 | SPAWN_WRITE_PIPE -- set aidescs element to pipe for parent to write | |
1838 | ||
1839 | If fkeepenv is FALSE, a standard environment is created. The | |
1840 | environment arguments (zpath, zuu_machine and zuu_user) are only | |
1841 | used if fkeepenv is FALSE; any of them may be NULL. */ | |
1842 | ||
1843 | pid_t | |
1844 | isspawn (pazargs, aidescs, fkeepuid, fkeepenv, zchdir, fnosigs, fshell, | |
1845 | zpath, zuu_machine, zuu_user) | |
1846 | const char **pazargs; | |
1847 | int aidescs[3]; | |
1848 | boolean fkeepuid; | |
1849 | boolean fkeepenv; | |
1850 | const char *zchdir; | |
1851 | boolean fnosigs; | |
1852 | boolean fshell; | |
1853 | const char *zpath; | |
1854 | const char *zuu_machine; | |
1855 | const char *zuu_user; | |
1856 | { | |
1857 | char *zshcmd = NULL; | |
1858 | int i; | |
1859 | char *azenv[9]; | |
1860 | char **pazenv; | |
1861 | boolean ferr; | |
1862 | int ierr = 0; | |
1863 | int onull; | |
1864 | int aichild_descs[3]; | |
1865 | int cpar_close; | |
1866 | int aipar_close[4]; | |
1867 | int cchild_close; | |
1868 | int aichild_close[3]; | |
1869 | pid_t iret = 0; | |
1870 | const char *zcmd; | |
1871 | ||
1872 | /* If we might have to use the shell, allocate enough space for the | |
1873 | quoted command before forking. Otherwise the allocation might | |
1874 | modify the data segment and we could not safely use vfork. */ | |
1875 | if (fshell) | |
1876 | { | |
1877 | int clen; | |
1878 | ||
1879 | clen = 0; | |
1880 | for (i = 0; pazargs[i] != NULL; i++) | |
1881 | clen += strlen (pazargs[i]); | |
1882 | zshcmd = (char *) alloca (2 * clen + i); | |
1883 | } | |
1884 | ||
1885 | /* Set up a standard environment. This is again done before forking | |
1886 | because it might modify the data segment. */ | |
1887 | ||
1888 | if (fkeepenv) | |
1889 | pazenv = environ; | |
1890 | else | |
1891 | { | |
1892 | const char *zterm, *ztz; | |
1893 | char *zspace; | |
1894 | int ienv; | |
1895 | ||
1896 | if (zpath == NULL) | |
1897 | zpath = CMDPATH; | |
1898 | ||
1899 | azenv[0] = (char *) alloca (sizeof "PATH=" + strlen (zpath)); | |
1900 | sprintf (azenv[0], "PATH=%s", zpath); | |
1901 | zspace = azenv[0] + sizeof "PATH=" - 1; | |
1902 | while ((zspace = strchr (zspace, ' ')) != NULL) | |
1903 | *zspace = ':'; | |
1904 | ||
1905 | azenv[1] = (char *) alloca (sizeof "HOME=" + strlen (zSpooldir)); | |
1906 | sprintf (azenv[1], "HOME=%s", zSpooldir); | |
1907 | ||
1908 | zterm = getenv ("TERM"); | |
1909 | if (zterm == NULL) | |
1910 | zterm = "unknown"; | |
1911 | azenv[2] = (char *) alloca (sizeof "TERM=" + strlen (zterm)); | |
1912 | sprintf (azenv[2], "TERM=%s", zterm); | |
1913 | ||
1914 | azenv[3] = (char *) "SHELL=/bin/sh"; | |
1915 | ||
1916 | azenv[4] = (char *) alloca (sizeof "USER=" + strlen (OWNER)); | |
1917 | sprintf (azenv[4], "USER=%s", OWNER); | |
1918 | ||
1919 | ienv = 5; | |
1920 | ||
1921 | ztz = getenv ("TZ"); | |
1922 | if (ztz != NULL) | |
1923 | { | |
1924 | azenv[ienv] = (char *) alloca (sizeof "TZ=" + strlen (ztz)); | |
1925 | sprintf (azenv[ienv], "TZ=%s", ztz); | |
1926 | ++ienv; | |
1927 | } | |
1928 | ||
1929 | if (zuu_machine != NULL) | |
1930 | { | |
1931 | azenv[ienv] = (char *) alloca (sizeof "UU_MACHINE=" | |
1932 | + strlen (zuu_machine)); | |
1933 | sprintf (azenv[ienv], "UU_MACHINE=%s", zuu_machine); | |
1934 | ++ienv; | |
1935 | } | |
1936 | ||
1937 | if (zuu_user != NULL) | |
1938 | { | |
1939 | azenv[ienv] = (char *) alloca (sizeof "UU_USER=" | |
1940 | + strlen (zuu_user)); | |
1941 | sprintf (azenv[ienv], "UU_USER=%s", zuu_user); | |
1942 | ++ienv; | |
1943 | } | |
1944 | ||
1945 | azenv[ienv] = NULL; | |
1946 | pazenv = azenv; | |
1947 | } | |
1948 | ||
1949 | /* Set up any needed pipes. */ | |
1950 | ||
1951 | ferr = FALSE; | |
1952 | onull = -1; | |
1953 | cpar_close = 0; | |
1954 | cchild_close = 0; | |
1955 | ||
1956 | for (i = 0; i < 3; i++) | |
1957 | { | |
1958 | if (aidescs[i] == SPAWN_NULL) | |
1959 | { | |
1960 | if (onull < 0) | |
1961 | { | |
1962 | onull = open ("/dev/null", O_RDWR); | |
1963 | if (onull < 0) | |
1964 | { | |
1965 | ierr = errno; | |
1966 | ferr = TRUE; | |
1967 | break; | |
1968 | } | |
1969 | aipar_close[cpar_close] = onull; | |
1970 | ++cpar_close; | |
1971 | } | |
1972 | aichild_descs[i] = onull; | |
1973 | } | |
1974 | else if (aidescs[i] != SPAWN_READ_PIPE | |
1975 | && aidescs[i] != SPAWN_WRITE_PIPE) | |
1976 | aichild_descs[i] = aidescs[i]; | |
1977 | else | |
1978 | { | |
1979 | int aipipe[2]; | |
1980 | ||
1981 | if (pipe (aipipe) < 0) | |
1982 | { | |
1983 | ierr = errno; | |
1984 | ferr = TRUE; | |
1985 | break; | |
1986 | } | |
1987 | ||
1988 | if (aidescs[i] == SPAWN_READ_PIPE) | |
1989 | { | |
1990 | aidescs[i] = aipipe[0]; | |
1991 | aichild_close[cchild_close] = aipipe[0]; | |
1992 | aichild_descs[i] = aipipe[1]; | |
1993 | aipar_close[cpar_close] = aipipe[1]; | |
1994 | } | |
1995 | else | |
1996 | { | |
1997 | aidescs[i] = aipipe[1]; | |
1998 | aichild_close[cchild_close] = aipipe[1]; | |
1999 | aichild_descs[i] = aipipe[0]; | |
2000 | aipar_close[cpar_close] = aipipe[0]; | |
2001 | } | |
2002 | ||
2003 | ++cpar_close; | |
2004 | ++cchild_close; | |
2005 | } | |
2006 | } | |
2007 | ||
2008 | #if DEBUG > 1 | |
2009 | if (! ferr && FDEBUGGING (DEBUG_EXECUTE)) | |
2010 | { | |
2011 | ulog (LOG_DEBUG_START, "Forking %s", pazargs[0]); | |
2012 | for (i = 1; pazargs[i] != NULL; i++) | |
2013 | ulog (LOG_DEBUG_CONTINUE, " %s", pazargs[i]); | |
2014 | ulog (LOG_DEBUG_END, "%s", ""); | |
2015 | } | |
2016 | #endif | |
2017 | ||
2018 | if (! ferr) | |
2019 | { | |
2020 | /* This should really be vfork if available. */ | |
2021 | iret = isfork (); | |
2022 | if (iret < 0) | |
2023 | { | |
2024 | ferr = TRUE; | |
2025 | ierr = errno; | |
2026 | } | |
2027 | } | |
2028 | ||
2029 | if (ferr) | |
2030 | { | |
2031 | for (i = 0; i < cpar_close; i++) | |
2032 | (void) close (aipar_close[i]); | |
2033 | for (i = 0; i < cchild_close; i++) | |
2034 | (void) close (aichild_close[i]); | |
2035 | errno = ierr; | |
2036 | return -1; | |
2037 | } | |
2038 | ||
2039 | /* Here the fork has succeeded and all the pipes have been done. */ | |
2040 | ||
2041 | if (iret != 0) | |
2042 | { | |
2043 | /* The parent. Close the child's ends of the pipes and return | |
2044 | the process ID. */ | |
2045 | for (i = 0; i < cpar_close; i++) | |
2046 | (void) close (aipar_close[i]); | |
2047 | return iret; | |
2048 | } | |
2049 | ||
2050 | /* The child. */ | |
2051 | ||
2052 | #ifdef STDIN_FILENO | |
2053 | #if STDIN_FILENO != 0 || STDOUT_FILENO != 1 || STDERR_FILENO != 2 | |
2054 | #error The following code makes invalid assumptions | |
2055 | #endif | |
2056 | #endif | |
2057 | ||
2058 | for (i = 0; i < 3; i++) | |
2059 | if (aichild_descs[i] != i) | |
2060 | (void) dup2 (aichild_descs[i], i); | |
2061 | ||
2062 | for (i = 3; i < cSdescriptors; i++) | |
2063 | (void) close (i); | |
2064 | ||
2065 | zcmd = pazargs[0]; | |
2066 | pazargs[0] = strrchr (zcmd, '/'); | |
2067 | if (pazargs[0] == NULL) | |
2068 | pazargs[0] = zcmd; | |
2069 | else | |
2070 | ++pazargs[0]; | |
2071 | ||
2072 | if (! fkeepuid) | |
2073 | { | |
2074 | (void) setuid (getuid ()); | |
2075 | (void) setgid (getgid ()); | |
2076 | } | |
2077 | ||
2078 | if (zchdir != NULL) | |
2079 | (void) chdir (zchdir); | |
2080 | ||
2081 | if (fnosigs) | |
2082 | { | |
2083 | #ifdef SIGHUP | |
2084 | (void) signal (SIGHUP, SIG_IGN); | |
2085 | #endif | |
2086 | #ifdef SIGINT | |
2087 | (void) signal (SIGINT, SIG_IGN); | |
2088 | #endif | |
2089 | #ifdef SIGQUIT | |
2090 | (void) signal (SIGQUIT, SIG_IGN); | |
2091 | #endif | |
2092 | } | |
2093 | ||
2094 | (void) execve ((char *) zcmd, (char **) pazargs, pazenv); | |
2095 | ||
2096 | /* The exec failed. If permitted, try using /bin/sh to execute a | |
2097 | shell script. */ | |
2098 | ||
2099 | if (errno == ENOEXEC && fshell) | |
2100 | { | |
2101 | char *zto; | |
2102 | const char *azshargs[4]; | |
2103 | ||
2104 | pazargs[0] = zcmd; | |
2105 | zto = zshcmd; | |
2106 | for (i = 0; pazargs[i] != NULL; i++) | |
2107 | { | |
2108 | const char *zfrom; | |
2109 | ||
2110 | for (zfrom = pazargs[i]; *zfrom != '\0'; zfrom++) | |
2111 | { | |
2112 | /* Some versions of /bin/sh appear to have a bug such | |
2113 | that quoting a '/' sometimes causes an error. I | |
2114 | don't know exactly when this happens (I can recreate | |
2115 | it on Ultrix 4.0), but in any case it is harmless to | |
2116 | not quote a '/'. */ | |
2117 | if (*zfrom != '/') | |
2118 | *zto++ = '\\'; | |
2119 | *zto++ = *zfrom; | |
2120 | } | |
2121 | *zto++ = ' '; | |
2122 | } | |
2123 | *(zto - 1) = '\0'; | |
2124 | ||
2125 | azshargs[0] = "sh"; | |
2126 | azshargs[1] = "-c"; | |
2127 | azshargs[2] = zshcmd; | |
2128 | azshargs[3] = NULL; | |
2129 | ||
2130 | (void) execve ((char *) "/bin/sh", (char **) azshargs, pazenv); | |
2131 | } | |
2132 | ||
2133 | _exit (EXIT_FAILURE); | |
2134 | ||
2135 | /* Avoid compiler warning. */ | |
2136 | return -1; | |
2137 | } | |
2138 | ||
2139 | /* A version of popen that goes through isspawn. This actually takes | |
2140 | an array of arguments rather than a string, and takes a boolean | |
2141 | read/write value rather than a string. It sets *pipid to the | |
2142 | process ID of the child. */ | |
2143 | ||
2144 | FILE * | |
2145 | espopen (pazargs, frd, pipid) | |
2146 | const char **pazargs; | |
2147 | boolean frd; | |
2148 | pid_t *pipid; | |
2149 | { | |
2150 | int aidescs[3]; | |
2151 | pid_t ipid; | |
2152 | FILE *eret; | |
2153 | ||
2154 | if (frd) | |
2155 | { | |
2156 | aidescs[0] = SPAWN_NULL; | |
2157 | aidescs[1] = SPAWN_READ_PIPE; | |
2158 | } | |
2159 | else | |
2160 | { | |
2161 | aidescs[0] = SPAWN_WRITE_PIPE; | |
2162 | aidescs[1] = SPAWN_NULL; | |
2163 | } | |
2164 | aidescs[2] = SPAWN_NULL; | |
2165 | ||
2166 | ipid = isspawn (pazargs, aidescs, FALSE, FALSE, | |
2167 | (const char *) NULL, FALSE, TRUE, | |
2168 | (const char *) NULL, (const char *) NULL, | |
2169 | (const char *) NULL); | |
2170 | if (ipid < 0) | |
2171 | return NULL; | |
2172 | ||
2173 | if (frd) | |
2174 | eret = fdopen (aidescs[1], (char *) "r"); | |
2175 | else | |
2176 | eret = fdopen (aidescs[0], (char *) "w"); | |
2177 | ||
2178 | if (eret == NULL) | |
2179 | { | |
2180 | int ierr; | |
2181 | ||
2182 | ierr = errno; | |
2183 | (void) close (frd ? aidescs[1] : aidescs[0]); | |
2184 | (void) kill (ipid, SIGKILL); | |
2185 | (void) iswait ((unsigned long) ipid, (const char *) NULL); | |
2186 | errno = ierr; | |
2187 | return NULL; | |
2188 | } | |
2189 | ||
2190 | *pipid = ipid; | |
2191 | ||
2192 | return eret; | |
2193 | } | |
2194 | ||
2195 | /* Wait for a particular process to finish. The ipid argument should | |
2196 | be pid_t, but then we couldn't have a prototype. If the zreport | |
2197 | argument is not NULL, then a wait error will be logged, and if the | |
2198 | exit status is non-zero it will be logged with zreport as the | |
2199 | header of the log message. If the zreport argument is NULL, no | |
2200 | errors will be logged. This function returns the exit status if | |
2201 | the process exited normally, or -1 on error or if the process was | |
2202 | killed by a signal (I don't just always return the exit status | |
2203 | because then the calling code would have to prepared to handle | |
2204 | union wait status vs. int status, and none of the callers care | |
2205 | which signal killed the program anyhow). | |
2206 | ||
2207 | This functions keeps waiting until the process finished, even if it | |
2208 | is interrupted by a signal. I think this is right for all uses. | |
2209 | The controversial one would be when called from uuxqt to wait for a | |
2210 | requested process. Hitting uuxqt with SIGKILL will approximate the | |
2211 | actions taken if we return from here with an error anyhow. If we | |
2212 | do get a signal, we call ulog with a NULL argument to get it in the | |
2213 | log file at about the right time. */ | |
2214 | ||
2215 | int | |
2216 | iswait (ipid, zreport) | |
2217 | unsigned long ipid; | |
2218 | const char *zreport; | |
2219 | { | |
2220 | wait_status istat; | |
2221 | ||
2222 | #if HAVE_WAITPID | |
2223 | while (waitpid ((pid_t) ipid, &istat, 0) < 0) | |
2224 | { | |
2225 | if (errno != EINTR) | |
2226 | { | |
2227 | if (zreport != NULL) | |
2228 | ulog (LOG_ERROR, "waitpid: %s", strerror (errno)); | |
2229 | return -1; | |
2230 | } | |
2231 | ulog (LOG_ERROR, (const char *) NULL); | |
2232 | } | |
2233 | #else /* ! HAVE_WAITPID */ | |
2234 | #if HAVE_WAIT4 | |
2235 | while (wait4 ((pid_t) ipid, &istat, 0, (struct rusage *) NULL) < 0) | |
2236 | { | |
2237 | if (errno != EINTR) | |
2238 | { | |
2239 | if (zreport != NULL) | |
2240 | ulog (LOG_ERROR, "wait4: %s", strerror (errno)); | |
2241 | return -1; | |
2242 | } | |
2243 | ulog (LOG_ERROR, (const char *) NULL); | |
2244 | } | |
2245 | #else /* ! HAVE_WAIT4 */ | |
2246 | pid_t igot; | |
2247 | ||
2248 | /* We could theoretically get the wrong child here if we're in some | |
2249 | kind of weird pipeline, so we don't give any error messages for | |
2250 | it. */ | |
2251 | while ((igot = wait (&istat)) != (pid_t) ipid) | |
2252 | { | |
2253 | if (igot < 0) | |
2254 | { | |
2255 | if (errno != EINTR) | |
2256 | { | |
2257 | if (zreport != NULL) | |
2258 | ulog (LOG_ERROR, "wait: %s", strerror (errno)); | |
2259 | return -1; | |
2260 | } | |
2261 | ulog (LOG_ERROR, (const char *) NULL); | |
2262 | } | |
2263 | } | |
2264 | #endif /* ! HAVE_WAIT4 */ | |
2265 | #endif /* ! HAVE_WAITPID */ | |
2266 | ||
2267 | DEBUG_MESSAGE2 (DEBUG_EXECUTE, "%s %d", | |
2268 | WIFEXITED (istat) ? "Exit status" : "Signal", | |
2269 | WIFEXITED (istat) ? WEXITSTATUS (istat) : WTERMSIG (istat)); | |
2270 | ||
2271 | if (WIFEXITED (istat) && WEXITSTATUS (istat) == 0) | |
2272 | return 0; | |
2273 | ||
2274 | if (zreport != NULL) | |
2275 | { | |
2276 | if (! WIFEXITED (istat)) | |
2277 | ulog (LOG_ERROR, "%s: Got signal %d", zreport, WTERMSIG (istat)); | |
2278 | else | |
2279 | ulog (LOG_ERROR, "%s: Exit status %d", zreport, | |
2280 | WEXITSTATUS (istat)); | |
2281 | } | |
2282 | ||
2283 | if (WIFEXITED (istat)) | |
2284 | return WEXITSTATUS (istat); | |
2285 | else | |
2286 | return -1; | |
2287 | } | |
2288 | \f | |
2289 | #if ! HAVE_REMOVE | |
2290 | ||
2291 | /* Remove a file. */ | |
2292 | ||
2293 | int | |
2294 | remove (z) | |
2295 | const char *z; | |
2296 | { | |
2297 | return unlink (z); | |
2298 | } | |
2299 | ||
2300 | #endif /* ! HAVE_REMOVE */ | |
2301 | ||
2302 | #if ! HAVE_STRERROR | |
2303 | ||
2304 | /* Some systems don't have a strerror definition, so we provide one. | |
2305 | This function is, of course, system dependent. */ | |
2306 | ||
2307 | char * | |
2308 | strerror (ierr) | |
2309 | int ierr; | |
2310 | { | |
2311 | extern int sys_nerr; | |
2312 | extern char *sys_errlist[]; | |
2313 | ||
2314 | if (ierr >= 0 && ierr < sys_nerr) | |
2315 | return sys_errlist[ierr]; | |
2316 | return (char *) "unknown error"; | |
2317 | } | |
2318 | ||
2319 | #endif /* ! HAVE_STRERROR */ | |
2320 | \f | |
2321 | #if ! HAVE_GETCWD && ! HAVE_GETWD | |
2322 | ||
2323 | /* Implement a simple getcwd that just calls /bin/pwd. I probably | |
2324 | should include Roland McGrath's getcwd implementation here, since | |
2325 | it doesn't fork, but it requires readdir support that I don't feel | |
2326 | like contemplating just now. */ | |
2327 | ||
2328 | static char * | |
2329 | getcwd (zbuf, cbuf) | |
2330 | char *zbuf; | |
2331 | int cbuf; | |
2332 | { | |
2333 | const char *azargs[2]; | |
2334 | FILE *e; | |
2335 | pid_t ipid; | |
2336 | int cread; | |
2337 | int ierr; | |
2338 | ||
2339 | azargs[0] = PWD_PROGRAM; | |
2340 | azargs[1] = NULL; | |
2341 | e = espopen (azargs, TRUE, &ipid); | |
2342 | if (e == NULL) | |
2343 | return NULL; | |
2344 | ||
2345 | ierr = 0; | |
2346 | ||
2347 | cread = fread (zbuf, sizeof (char), cbuf, e); | |
2348 | if (cread == 0) | |
2349 | ierr = errno; | |
2350 | ||
2351 | (void) fclose (e); | |
2352 | ||
2353 | if (iswait ((unsigned long) ipid, (const char *) NULL) != 0) | |
2354 | { | |
2355 | ierr = EACCES; | |
2356 | cread = 0; | |
2357 | } | |
2358 | ||
2359 | if (cread != 0) | |
2360 | { | |
2361 | if (zbuf[cread - 1] == '\n') | |
2362 | zbuf[cread - 1] = '\0'; | |
2363 | else | |
2364 | { | |
2365 | ierr = ERANGE; | |
2366 | cread = 0; | |
2367 | } | |
2368 | } | |
2369 | ||
2370 | if (cread == 0) | |
2371 | { | |
2372 | errno = ierr; | |
2373 | return NULL; | |
2374 | } | |
2375 | ||
2376 | return zbuf; | |
2377 | } | |
2378 | ||
2379 | #endif /* ! HAVE_GETCWD && ! HAVE_GETWD */ | |
2380 | \f | |
2381 | #if ! HAVE_DUP2 | |
2382 | ||
2383 | /* Emulate the dup2 call. I basically took this from the emacs 18.57 | |
2384 | distribution, although I cleaned it up a bit and made it POSIX | |
2385 | compliant. */ | |
2386 | ||
2387 | int | |
2388 | dup2 (oold, onew) | |
2389 | int oold; | |
2390 | int onew; | |
2391 | { | |
2392 | if (oold == onew) | |
2393 | return onew; | |
2394 | (void) close (onew); | |
2395 | ||
2396 | #ifdef F_DUPFD | |
2397 | return fcntl (oold, F_DUPFD, onew); | |
2398 | #else | |
2399 | { | |
2400 | int onext, oret, isave; | |
2401 | ||
2402 | onext = dup (oold); | |
2403 | if (onext == onew) | |
2404 | return onext; | |
2405 | if (onext < 0) | |
2406 | return -1; | |
2407 | oret = dup2 (oold, onew); | |
2408 | isave = errno; | |
2409 | (void) close (onext); | |
2410 | errno = isave; | |
2411 | return oret; | |
2412 | } | |
2413 | #endif | |
2414 | } | |
2415 | ||
2416 | #endif /* ! HAVE_DUP2 */ | |
2417 | \f | |
2418 | #if ! HAVE_OPENDIR | |
2419 | ||
2420 | /* Simple emulations of opendir/readdir/closedir for systems which | |
2421 | have the original format of Unix directories. It's probably better | |
2422 | to get Doug Gwyn's public domain set of emulation functions. */ | |
2423 | ||
2424 | DIR * | |
2425 | opendir (zdir) | |
2426 | const char *zdir; | |
2427 | { | |
2428 | int o; | |
2429 | struct stat s; | |
2430 | DIR *qret; | |
2431 | ||
2432 | o = open (zdir, O_RDONLY | O_NOCTTY, 0); | |
2433 | if (o == -1) | |
2434 | return NULL; | |
2435 | if (fstat (o, &s) < 0) | |
2436 | { | |
2437 | (void) close (o); | |
2438 | return NULL; | |
2439 | } | |
2440 | if (! S_ISDIR (s.st_mode)) | |
2441 | { | |
2442 | (void) close (o); | |
2443 | errno = ENOTDIR; | |
2444 | return NULL; | |
2445 | } | |
2446 | qret = (DIR *) xmalloc (sizeof (DIR)); | |
2447 | qret->o = o; | |
2448 | return qret; | |
2449 | } | |
2450 | ||
2451 | struct dirent * | |
2452 | readdir (q) | |
2453 | DIR *q; | |
2454 | { | |
2455 | struct direct sdir; | |
2456 | int cgot; | |
2457 | ||
2458 | do | |
2459 | { | |
2460 | cgot = read (q->o, &sdir, sizeof (struct direct)); | |
2461 | if (cgot <= 0) | |
2462 | return NULL; | |
2463 | if (cgot != sizeof (struct direct)) | |
2464 | { | |
2465 | errno = ENOENT; | |
2466 | return NULL; | |
2467 | } | |
2468 | } | |
2469 | while (sdir.d_ino == 0); | |
2470 | ||
2471 | strncpy (q->s.d_name, sdir.d_name, DIRSIZ); | |
2472 | q->s.d_name[DIRSIZ] = '\0'; | |
2473 | return &q->s; | |
2474 | } | |
2475 | ||
2476 | int | |
2477 | closedir (q) | |
2478 | DIR *q; | |
2479 | { | |
2480 | int iret, isave; | |
2481 | ||
2482 | iret = close (q->o); | |
2483 | isave = errno; | |
2484 | xfree (q); | |
2485 | errno = isave; | |
2486 | return iret; | |
2487 | } | |
2488 | ||
2489 | #endif /* ! HAVE_OPENDIR */ | |
2490 | \f | |
2491 | #if ! HAVE_MKDIR | |
2492 | ||
2493 | /* We don't have the mkdir system call, so we invoke the suid program | |
2494 | uudir to create the directory with the correct owner. */ | |
2495 | ||
2496 | int | |
2497 | mkdir (zdir, imode) | |
2498 | const char *zdir; | |
2499 | int imode; | |
2500 | { | |
2501 | const char *azargs[2]; | |
2502 | int aidescs[3]; | |
2503 | pid_t ipid; | |
2504 | ||
2505 | /* /bin/mkdir will create the directory with mode 777, so we set our | |
2506 | umask to get the mode we want. */ | |
2507 | (void) umask ((~ imode) & (S_IRWXU | S_IRWXG | S_IRWXO)); | |
2508 | ||
2509 | azargs[0] = UUDIR_PROGRAM; | |
2510 | azargs[1] = NULL; | |
2511 | aidescs[0] = SPAWN_NULL; | |
2512 | aidescs[1] = SPAWN_NULL; | |
2513 | aidescs[2] = SPAWN_NULL; | |
2514 | ||
2515 | ipid = isspawn (azargs, aidescs, FALSE, FALSE, (const char *) NULL, | |
2516 | TRUE, FALSE, (const char *) NULL, | |
2517 | (const char *) NULL, (const char *) NULL); | |
2518 | ||
2519 | (void) umask (0); | |
2520 | ||
2521 | if (ipid < 0) | |
2522 | return -1; | |
2523 | ||
2524 | if (iswait ((unsigned long) ipid, (const char *) NULL) != 0) | |
2525 | { | |
2526 | /* Make up an errno value. */ | |
2527 | errno = EACCES; | |
2528 | return -1; | |
2529 | } | |
2530 | ||
2531 | return 0; | |
2532 | } | |
2533 | ||
2534 | #endif /* ! HAVE_MKDIR */ | |
2535 | \f | |
2536 | /* | |
2537 | Local variables: | |
2538 | mode:c | |
2539 | End: | |
2540 | */ |