File: | src/usr.bin/rdist/common.c |
Warning: | line 623, column 8 Although the value stored to 'len' is used in the enclosing expression, the value is never actually read from 'len' |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* $OpenBSD: common.c,v 1.41 2022/12/26 19:16:02 jmc Exp $ */ |
2 | |
3 | /* |
4 | * Copyright (c) 1983 Regents of the University of California. |
5 | * All rights reserved. |
6 | * |
7 | * Redistribution and use in source and binary forms, with or without |
8 | * modification, are permitted provided that the following conditions |
9 | * are met: |
10 | * 1. Redistributions of source code must retain the above copyright |
11 | * notice, this list of conditions and the following disclaimer. |
12 | * 2. Redistributions in binary form must reproduce the above copyright |
13 | * notice, this list of conditions and the following disclaimer in the |
14 | * documentation and/or other materials provided with the distribution. |
15 | * 3. Neither the name of the University nor the names of its contributors |
16 | * may be used to endorse or promote products derived from this software |
17 | * without specific prior written permission. |
18 | * |
19 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
20 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
23 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
24 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
25 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
26 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
27 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
28 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
29 | * SUCH DAMAGE. |
30 | */ |
31 | |
32 | #include <sys/stat.h> |
33 | #include <sys/time.h> |
34 | #include <sys/wait.h> |
35 | |
36 | #include <errno(*__errno()).h> |
37 | #include <fcntl.h> |
38 | #include <grp.h> |
39 | #include <limits.h> |
40 | #include <paths.h> |
41 | #include <stdarg.h> |
42 | #include <stdio.h> |
43 | #include <stdlib.h> |
44 | #include <string.h> |
45 | #include <unistd.h> |
46 | |
47 | #include "defs.h" |
48 | |
49 | /* |
50 | * Things common to both the client and server. |
51 | */ |
52 | |
53 | /* |
54 | * Variables common to both client and server |
55 | */ |
56 | char host[HOST_NAME_MAX255+1]; /* Name of this host */ |
57 | uid_t userid = (uid_t)-1; /* User's UID */ |
58 | gid_t groupid = (gid_t)-1; /* User's GID */ |
59 | gid_t gidset[NGROUPS_MAX16]; /* User's GID list */ |
60 | int gidsetlen = 0; /* Number of GIDS in list */ |
61 | char *homedir = NULL((void *)0); /* User's $HOME */ |
62 | char *locuser = NULL((void *)0); /* Local User's name */ |
63 | int isserver = FALSE0; /* We're the server */ |
64 | int amchild = 0; /* This PID is a child */ |
65 | int do_fork = 1; /* Fork child process */ |
66 | char *currenthost = NULL((void *)0); /* Current client hostname */ |
67 | char *progname = NULL((void *)0); /* Name of this program */ |
68 | int rem_r = -1; /* Client file descriptor */ |
69 | int rem_w = -1; /* Client file descriptor */ |
70 | volatile sig_atomic_t contimedout = FALSE0; /* Connection timed out */ |
71 | int rtimeout = RTIMEOUT900; /* Response time out */ |
72 | jmp_buf finish_jmpbuf; /* Finish() jmp buffer */ |
73 | int setjmp_ok = FALSE0; /* setjmp()/longjmp() status */ |
74 | char **realargv; /* Real main() argv */ |
75 | int realargc; /* Real main() argc */ |
76 | opt_t options = 0; /* Global install options */ |
77 | char defowner[64] = "bin"; /* Default owner */ |
78 | char defgroup[64] = "bin"; /* Default group */ |
79 | |
80 | static int sendcmdmsg(int, char *, size_t); |
81 | static ssize_t remread(int, u_char *, size_t); |
82 | static int remmore(void); |
83 | |
84 | /* |
85 | * Front end to write() that handles partial write() requests. |
86 | */ |
87 | ssize_t |
88 | xwrite(int fd, void *buf, size_t len) |
89 | { |
90 | size_t nleft = len; |
91 | ssize_t nwritten; |
92 | char *ptr = buf; |
93 | |
94 | while (nleft > 0) { |
95 | if ((nwritten = write(fd, ptr, nleft)) <= 0) { |
96 | return nwritten; |
97 | } |
98 | nleft -= nwritten; |
99 | ptr += nwritten; |
100 | } |
101 | |
102 | return len; |
103 | } |
104 | |
105 | /* |
106 | * Do run-time initialization |
107 | */ |
108 | int |
109 | init(int argc, char **argv, char **envp) |
110 | { |
111 | struct passwd *pw; |
112 | int i; |
113 | |
114 | /* |
115 | * Save a copy of our argc and argv before setargs() overwrites them |
116 | */ |
117 | realargc = argc; |
118 | realargv = xmalloc(sizeof(char *) * (argc+1)); |
119 | for (i = 0; i < argc; i++) |
120 | realargv[i] = xstrdup(argv[i]); |
121 | |
122 | pw = getpwuid(userid = getuid()); |
123 | if (pw == NULL((void *)0)) { |
124 | error("Your user id (%u) is not known to this system.", |
125 | getuid()); |
126 | return(-1); |
127 | } |
128 | |
129 | debugmsg(DM_MISC0x10, "UserID = %u pwname = '%s' home = '%s'\n", |
130 | userid, pw->pw_name, pw->pw_dir); |
131 | homedir = xstrdup(pw->pw_dir); |
132 | locuser = xstrdup(pw->pw_name); |
133 | groupid = pw->pw_gid; |
134 | gidsetlen = getgroups(NGROUPS_MAX16, gidset); |
135 | gethostname(host, sizeof(host)); |
136 | #if 0 |
137 | if ((cp = strchr(host, '.')) != NULL((void *)0)) |
138 | *cp = CNULL'\0'; |
139 | #endif |
140 | |
141 | /* |
142 | * If we're not root, disable paranoid ownership checks |
143 | * since normal users cannot chown() files. |
144 | */ |
145 | if (!isserver && userid != 0) { |
146 | FLAG_ON(options, DO_NOCHKOWNER)options |= 0x0004000; |
147 | FLAG_ON(options, DO_NOCHKGROUP)options |= 0x0010000; |
148 | } |
149 | |
150 | return(0); |
151 | } |
152 | |
153 | /* |
154 | * Finish things up before ending. |
155 | */ |
156 | void |
157 | finish(void) |
158 | { |
159 | debugmsg(DM_CALL0x01, |
160 | "finish() called: do_fork = %d amchild = %d isserver = %d", |
161 | do_fork, amchild, isserver); |
162 | cleanup(0); |
163 | |
164 | /* |
165 | * There's no valid finish_jmpbuf for the rdist master parent. |
166 | */ |
167 | if (!do_fork || amchild || isserver) { |
168 | |
169 | if (!setjmp_ok) { |
170 | #ifdef DEBUG_SETJMP |
171 | error("attempting longjmp() without target"); |
172 | abort(); |
173 | #else |
174 | exit(1); |
175 | #endif |
176 | } |
177 | |
178 | longjmp(finish_jmpbuf, 1); |
179 | /*NOTREACHED*/ |
180 | error("Unexpected failure of longjmp() in finish()"); |
181 | exit(2); |
182 | } else |
183 | exit(1); |
184 | } |
185 | |
186 | /* |
187 | * Handle lost connections |
188 | */ |
189 | void |
190 | lostconn(void) |
191 | { |
192 | /* Prevent looping */ |
193 | (void) signal(SIGPIPE13, SIG_IGN(void (*)(int))1); |
194 | |
195 | rem_r = rem_w = -1; /* Ensure we don't try to send to server */ |
196 | checkhostname(); |
197 | error("Lost connection to %s", |
198 | (currenthost) ? currenthost : "(unknown)"); |
199 | |
200 | finish(); |
201 | } |
202 | |
203 | /* |
204 | * General signal handler |
205 | */ |
206 | void |
207 | sighandler(int sig) |
208 | { |
209 | int save_errno = errno(*__errno()); |
210 | |
211 | /* XXX signal race */ |
212 | debugmsg(DM_CALL0x01, "sighandler() received signal %d\n", sig); |
213 | |
214 | switch (sig) { |
215 | case SIGALRM14: |
216 | contimedout = TRUE1; |
217 | /* XXX signal race */ |
218 | checkhostname(); |
219 | error("Response time out"); |
220 | finish(); |
221 | break; |
222 | |
223 | case SIGPIPE13: |
224 | /* XXX signal race */ |
225 | lostconn(); |
226 | break; |
227 | |
228 | case SIGFPE8: |
229 | debug = !debug; |
230 | break; |
231 | |
232 | case SIGHUP1: |
233 | case SIGINT2: |
234 | case SIGQUIT3: |
235 | case SIGTERM15: |
236 | /* XXX signal race */ |
237 | finish(); |
238 | break; |
239 | |
240 | default: |
241 | /* XXX signal race */ |
242 | fatalerr("No signal handler defined for signal %d.", sig); |
243 | } |
244 | errno(*__errno()) = save_errno; |
245 | } |
246 | |
247 | /* |
248 | * Function to actually send the command char and message to the |
249 | * remote host. |
250 | */ |
251 | static int |
252 | sendcmdmsg(int cmd, char *msg, size_t msgsize) |
253 | { |
254 | int len; |
255 | |
256 | if (rem_w < 0) |
257 | return(-1); |
258 | |
259 | /* |
260 | * All commands except C_NONE should have a newline |
261 | */ |
262 | if (cmd != C_NONE'=' && !strchr(msg + 1, '\n')) |
263 | (void) strlcat(msg + 1, "\n", msgsize - 1); |
264 | |
265 | if (cmd == C_NONE'=') |
266 | len = strlen(msg); |
267 | else { |
268 | len = strlen(msg + 1) + 1; |
269 | msg[0] = cmd; |
270 | } |
271 | |
272 | debugmsg(DM_PROTO0x02, ">>> Cmd = %c (\\%3.3o) Msg = \"%.*s\"", |
273 | cmd, cmd, |
274 | (cmd == C_NONE'=') ? len-1 : len-2, |
275 | (cmd == C_NONE'=') ? msg : msg + 1); |
276 | |
277 | return(!(xwrite(rem_w, msg, len) == len)); |
278 | } |
279 | |
280 | /* |
281 | * Send a command message to the remote host. |
282 | * Called as sendcmd(char cmdchar, char *fmt, arg1, arg2, ...) |
283 | * The fmt may be NULL, in which case there are no args. |
284 | */ |
285 | int |
286 | sendcmd(char cmd, const char *fmt, ...) |
287 | { |
288 | static char buf[BUFSIZ1024]; |
289 | va_list args; |
290 | |
291 | va_start(args, fmt)__builtin_va_start((args), fmt); |
292 | if (fmt) |
293 | (void) vsnprintf(buf + (cmd != C_NONE'='), |
294 | sizeof(buf) - (cmd != C_NONE'='), fmt, args); |
295 | else |
296 | buf[1] = CNULL'\0'; |
297 | va_end(args)__builtin_va_end((args)); |
298 | |
299 | return(sendcmdmsg(cmd, buf, sizeof(buf))); |
300 | } |
301 | |
302 | /* |
303 | * Internal variables and routines for reading lines from the remote. |
304 | */ |
305 | static u_char rembuf[BUFSIZ1024]; |
306 | static u_char *remptr; |
307 | static ssize_t remleft; |
308 | |
309 | #define remc()(--remleft < 0 ? remmore() : *remptr++) (--remleft < 0 ? remmore() : *remptr++) |
310 | |
311 | /* |
312 | * Back end to remote read() |
313 | */ |
314 | static ssize_t |
315 | remread(int fd, u_char *buf, size_t bufsiz) |
316 | { |
317 | return(read(fd, (char *)buf, bufsiz)); |
318 | } |
319 | |
320 | static int |
321 | remmore(void) |
322 | { |
323 | (void) signal(SIGALRM14, sighandler); |
324 | (void) alarm(rtimeout); |
325 | |
326 | remleft = remread(rem_r, rembuf, sizeof(rembuf)); |
327 | |
328 | (void) alarm(0); |
329 | |
330 | if (remleft < 0) |
331 | return (-2); /* error */ |
332 | if (remleft == 0) |
333 | return (-1); /* EOF */ |
334 | remptr = rembuf; |
335 | remleft--; |
336 | return (*remptr++); |
337 | } |
338 | |
339 | /* |
340 | * Read an input line from the remote. Return the number of bytes |
341 | * stored (equivalent to strlen(p)). If `cleanup' is set, EOF at |
342 | * the beginning of a line is returned as EOF (-1); other EOFs, or |
343 | * errors, call cleanup() or lostconn(). In other words, unless |
344 | * the third argument is nonzero, this routine never returns failure. |
345 | */ |
346 | int |
347 | remline(u_char *buffer, int space, int doclean) |
348 | { |
349 | int c, left = space; |
350 | u_char *p = buffer; |
351 | |
352 | if (rem_r < 0) { |
353 | error("Cannot read remote input: Remote descriptor not open."); |
354 | return(-1); |
355 | } |
356 | |
357 | while (left > 0) { |
358 | if ((c = remc()(--remleft < 0 ? remmore() : *remptr++)) < -1) { /* error */ |
359 | if (doclean) { |
360 | finish(); |
361 | /*NOTREACHED*/ |
362 | } |
363 | lostconn(); |
364 | /*NOTREACHED*/ |
365 | } |
366 | if (c == -1) { /* got EOF */ |
367 | if (doclean) { |
368 | if (left == space) |
369 | return (-1);/* signal proper EOF */ |
370 | finish(); /* improper EOF */ |
371 | /*NOTREACHED*/ |
372 | } |
373 | lostconn(); |
374 | /*NOTREACHED*/ |
375 | } |
376 | if (c == '\n') { |
377 | *p = CNULL'\0'; |
378 | |
379 | if (debug) { |
380 | static char mbuf[BUFSIZ1024]; |
381 | |
382 | (void) snprintf(mbuf, sizeof(mbuf), |
383 | "<<< Cmd = %c (\\%3.3o) Msg = \"%s\"", |
384 | buffer[0], buffer[0], |
385 | buffer + 1); |
386 | |
387 | debugmsg(DM_PROTO0x02, "%s", mbuf); |
388 | } |
389 | |
390 | return (space - left); |
391 | } |
392 | *p++ = c; |
393 | left--; |
394 | } |
395 | |
396 | /* this will probably blow the entire session */ |
397 | error("remote input line too long"); |
398 | p[-1] = CNULL'\0'; /* truncate */ |
399 | return (space); |
400 | } |
401 | |
402 | /* |
403 | * Non-line-oriented remote read. |
404 | */ |
405 | ssize_t |
406 | readrem(char *p, ssize_t space) |
407 | { |
408 | if (remleft <= 0) { |
409 | /* |
410 | * Set remote time out alarm. |
411 | */ |
412 | (void) signal(SIGALRM14, sighandler); |
413 | (void) alarm(rtimeout); |
414 | |
415 | remleft = remread(rem_r, rembuf, sizeof(rembuf)); |
416 | |
417 | (void) alarm(0); |
418 | remptr = rembuf; |
419 | } |
420 | |
421 | if (remleft <= 0) |
422 | return (remleft); |
423 | if (remleft < space) |
424 | space = remleft; |
425 | |
426 | memcpy(p, remptr, space); |
427 | |
428 | remptr += space; |
429 | remleft -= space; |
430 | |
431 | return (space); |
432 | } |
433 | |
434 | /* |
435 | * Get the user name for the uid. |
436 | */ |
437 | char * |
438 | getusername(uid_t uid, char *file, opt_t opts) |
439 | { |
440 | static char buf[100]; |
441 | static uid_t lastuid = (uid_t)-1; |
442 | const char *name; |
443 | |
444 | /* |
445 | * The value of opts may have changed so we always |
446 | * do the opts check. |
447 | */ |
448 | if (IS_ON(opts, DO_NUMCHKOWNER)(opts & 0x0080000)) { |
449 | (void) snprintf(buf, sizeof(buf), ":%u", uid); |
450 | return(buf); |
451 | } |
452 | |
453 | /* |
454 | * Try to avoid passwd lookup. |
455 | */ |
456 | if (lastuid == uid && buf[0] != '\0' && buf[0] != ':') |
457 | return(buf); |
458 | |
459 | lastuid = uid; |
460 | |
461 | if ((name = user_from_uid(uid, 1)) == NULL((void *)0)) { |
462 | if (IS_ON(opts, DO_DEFOWNER)(opts & 0x0800000) && !isserver) |
463 | (void) strlcpy(buf, defowner, sizeof(buf)); |
464 | else { |
465 | message(MT_WARNING0x0010, |
466 | "%s: No password entry for uid %u", file, uid); |
467 | (void) snprintf(buf, sizeof(buf), ":%u", uid); |
468 | } |
469 | } else { |
470 | (void) strlcpy(buf, name, sizeof(buf)); |
471 | } |
472 | |
473 | return(buf); |
474 | } |
475 | |
476 | /* |
477 | * Get the group name for the gid. |
478 | */ |
479 | char * |
480 | getgroupname(gid_t gid, char *file, opt_t opts) |
481 | { |
482 | static char buf[100]; |
483 | static gid_t lastgid = (gid_t)-1; |
484 | const char *name; |
485 | |
486 | /* |
487 | * The value of opts may have changed so we always |
488 | * do the opts check. |
489 | */ |
490 | if (IS_ON(opts, DO_NUMCHKGROUP)(opts & 0x0040000)) { |
491 | (void) snprintf(buf, sizeof(buf), ":%u", gid); |
492 | return(buf); |
493 | } |
494 | |
495 | /* |
496 | * Try to avoid group lookup. |
497 | */ |
498 | if (lastgid == gid && buf[0] != '\0' && buf[0] != ':') |
499 | return(buf); |
500 | |
501 | lastgid = gid; |
502 | |
503 | if ((name = group_from_gid(gid, 1)) == NULL((void *)0)) { |
504 | if (IS_ON(opts, DO_DEFGROUP)(opts & 0x0400000) && !isserver) |
505 | (void) strlcpy(buf, defgroup, sizeof(buf)); |
506 | else { |
507 | message(MT_WARNING0x0010, "%s: No name for group %u", |
508 | file, gid); |
509 | (void) snprintf(buf, sizeof(buf), ":%u", gid); |
510 | } |
511 | } else |
512 | (void) strlcpy(buf, name, sizeof(buf)); |
513 | |
514 | return(buf); |
515 | } |
516 | |
517 | /* |
518 | * Read a response from the remote host. |
519 | */ |
520 | int |
521 | response(void) |
522 | { |
523 | static u_char resp[BUFSIZ1024]; |
524 | u_char *s; |
525 | int n; |
526 | |
527 | debugmsg(DM_CALL0x01, "response() start\n"); |
528 | |
529 | n = remline(s = resp, sizeof(resp), 0); |
530 | |
531 | n--; |
532 | switch (*s++) { |
533 | case C_ACK'\5': |
534 | debugmsg(DM_PROTO0x02, "received ACK\n"); |
535 | return(0); |
536 | case C_LOGMSG'\4': |
537 | if (n > 0) { |
538 | message(MT_CHANGE0x0020, "%s", s); |
539 | return(1); |
540 | } |
541 | debugmsg(DM_PROTO0x02, "received EMPTY logmsg\n"); |
542 | return(0); |
543 | case C_NOTEMSG'\3': |
544 | if (s) |
545 | message(MT_NOTICE0x0100, "%s", s); |
546 | return(response()); |
547 | |
548 | default: |
549 | s--; |
550 | n++; |
551 | /* fall into... */ |
552 | |
553 | case C_ERRMSG'\1': /* Normal error message */ |
554 | if (s) |
555 | message(MT_NERROR0x0002, "%s", s); |
556 | return(-1); |
557 | |
558 | case C_FERRMSG'\2': /* Fatal error message */ |
559 | if (s) |
560 | message(MT_FERROR0x0004, "%s", s); |
561 | finish(); |
562 | return(-1); |
563 | } |
564 | /*NOTREACHED*/ |
565 | } |
566 | |
567 | /* |
568 | * This should be in expand.c but the other routines call other modules |
569 | * that we don't want to load in. |
570 | * |
571 | * Expand file names beginning with `~' into the |
572 | * user's home directory path name. Return a pointer in buf to the |
573 | * part corresponding to `file'. |
574 | */ |
575 | char * |
576 | exptilde(char *ebuf, char *file, size_t ebufsize) |
577 | { |
578 | struct passwd *pw; |
579 | char *pw_dir, *rest; |
580 | static char lastuser[_PW_NAME_LEN31 + 1]; |
581 | static char lastdir[PATH_MAX1024]; |
582 | size_t len; |
583 | |
584 | if (*file != '~') { |
585 | notilde: |
586 | (void) strlcpy(ebuf, file, ebufsize); |
587 | return(ebuf); |
588 | } |
589 | pw_dir = homedir; |
590 | if (*++file == CNULL'\0') { |
591 | rest = NULL((void *)0); |
592 | } else if (*file == '/') { |
593 | rest = file; |
594 | } else { |
595 | rest = file; |
596 | while (*rest && *rest != '/') |
597 | rest++; |
598 | if (*rest == '/') |
599 | *rest = CNULL'\0'; |
600 | else |
601 | rest = NULL((void *)0); |
602 | if (strcmp(locuser, file) != 0) { |
603 | if (strcmp(lastuser, file) != 0) { |
604 | if ((pw = getpwnam(file)) == NULL((void *)0)) { |
605 | error("%s: unknown user name", file); |
606 | if (rest != NULL((void *)0)) |
607 | *rest = '/'; |
608 | return(NULL((void *)0)); |
609 | } |
610 | strlcpy(lastuser, pw->pw_name, sizeof(lastuser)); |
611 | strlcpy(lastdir, pw->pw_dir, sizeof(lastdir)); |
612 | } |
613 | pw_dir = lastdir; |
614 | } |
615 | if (rest != NULL((void *)0)) |
616 | *rest = '/'; |
617 | } |
618 | if ((len = strlcpy(ebuf, pw_dir, ebufsize)) >= ebufsize) |
619 | goto notilde; |
620 | pw_dir = ebuf + len; |
621 | if (rest != NULL((void *)0)) { |
622 | pw_dir++; |
623 | if ((len = strlcat(ebuf, rest, ebufsize)) >= ebufsize) |
Although the value stored to 'len' is used in the enclosing expression, the value is never actually read from 'len' | |
624 | goto notilde; |
625 | } |
626 | return(pw_dir); |
627 | } |
628 | |
629 | |
630 | |
631 | /* |
632 | * Set access and modify times of a given file |
633 | */ |
634 | int |
635 | setfiletime(char *file, time_t atime, time_t mtime) |
636 | { |
637 | struct timeval tv[2]; |
638 | |
639 | if (atime != 0 && mtime != 0) { |
640 | tv[0].tv_sec = atime; |
641 | tv[1].tv_sec = mtime; |
642 | tv[0].tv_usec = tv[1].tv_usec = 0; |
643 | return (utimes(file, tv)); |
644 | } else /* Set to current time */ |
645 | return (utimes(file, NULL((void *)0))); |
646 | } |
647 | |
648 | /* |
649 | * Get version info |
650 | */ |
651 | char * |
652 | getversion(void) |
653 | { |
654 | static char buff[BUFSIZ1024]; |
655 | |
656 | (void) snprintf(buff, sizeof(buff), |
657 | "Version %s.%d (%s) - Protocol Version %d, Release %s, Patch level %d", |
658 | DISTVERSION"0.92", PATCHLEVEL0, DISTSTATUS"BETA", |
659 | VERSION6, DISTVERSION"0.92", PATCHLEVEL0); |
660 | |
661 | return(buff); |
662 | } |
663 | |
664 | /* |
665 | * Execute a shell command to handle special cases. |
666 | * This is now common to both server and client |
667 | */ |
668 | void |
669 | runcommand(char *cmd) |
670 | { |
671 | ssize_t nread; |
672 | pid_t pid, wpid; |
673 | char *cp, *s; |
674 | char sbuf[BUFSIZ1024], buf[BUFSIZ1024]; |
675 | int fd[2], status; |
676 | |
677 | if (pipe(fd) == -1) { |
678 | error("pipe of %s failed: %s", cmd, SYSERRstrerror((*__errno()))); |
679 | return; |
680 | } |
681 | |
682 | if ((pid = fork()) == 0) { |
683 | /* |
684 | * Return everything the shell commands print. |
685 | */ |
686 | (void) close(0); |
687 | (void) close(1); |
688 | (void) close(2); |
689 | (void) open(_PATH_DEVNULL"/dev/null", O_RDONLY0x0000); |
690 | (void) dup(fd[PIPE_WRITE1]); |
691 | (void) dup(fd[PIPE_WRITE1]); |
692 | (void) close(fd[PIPE_READ0]); |
693 | (void) close(fd[PIPE_WRITE1]); |
694 | (void) execl(_PATH_BSHELL"/bin/sh", "sh", "-c", cmd, (char *)NULL((void *)0)); |
695 | _exit(127); |
696 | } |
697 | (void) close(fd[PIPE_WRITE1]); |
698 | s = sbuf; |
699 | *s++ = C_LOGMSG'\4'; |
700 | while ((nread = read(fd[PIPE_READ0], buf, sizeof(buf))) > 0) { |
701 | cp = buf; |
702 | do { |
703 | *s++ = *cp++; |
704 | if (cp[-1] != '\n') { |
705 | if (s < (char *) &sbuf[sizeof(sbuf)-1]) |
706 | continue; |
707 | *s++ = '\n'; |
708 | } |
709 | /* |
710 | * Throw away blank lines. |
711 | */ |
712 | if (s == &sbuf[2]) { |
713 | s--; |
714 | continue; |
715 | } |
716 | if (isserver) |
717 | (void) xwrite(rem_w, sbuf, s - sbuf); |
718 | else { |
719 | *s = CNULL'\0'; |
720 | message(MT_INFO0x0040, "%s", sbuf+1); |
721 | } |
722 | s = &sbuf[1]; |
723 | } while (--nread); |
724 | } |
725 | if (s > (char *) &sbuf[1]) { |
726 | *s++ = '\n'; |
727 | if (isserver) |
728 | (void) xwrite(rem_w, sbuf, s - sbuf); |
729 | else { |
730 | *s = CNULL'\0'; |
731 | message(MT_INFO0x0040, "%s", sbuf+1); |
732 | } |
733 | } |
734 | while ((wpid = wait(&status)) != pid && wpid != -1) |
735 | ; |
736 | if (wpid == -1) |
737 | status = -1; |
738 | (void) close(fd[PIPE_READ0]); |
739 | if (status) |
740 | error("shell returned %d", status); |
741 | else if (isserver) |
742 | ack()(void) sendcmd('\5', ((void *)0)); |
743 | } |
744 | |
745 | /* |
746 | * Malloc with error checking |
747 | */ |
748 | void * |
749 | xmalloc(size_t amt) |
750 | { |
751 | void *ptr; |
752 | |
753 | if ((ptr = malloc(amt)) == NULL((void *)0)) |
754 | fatalerr("Cannot malloc %zu bytes of memory.", amt); |
755 | |
756 | return (ptr); |
757 | } |
758 | |
759 | /* |
760 | * realloc with error checking |
761 | */ |
762 | void * |
763 | xrealloc(void *baseptr, size_t amt) |
764 | { |
765 | void *new; |
766 | |
767 | if ((new = realloc(baseptr, amt)) == NULL((void *)0)) |
768 | fatalerr("Cannot realloc %zu bytes of memory.", amt); |
769 | |
770 | return (new); |
771 | } |
772 | |
773 | /* |
774 | * calloc with error checking |
775 | */ |
776 | void * |
777 | xcalloc(size_t num, size_t esize) |
778 | { |
779 | void *ptr; |
780 | |
781 | if ((ptr = calloc(num, esize)) == NULL((void *)0)) |
782 | fatalerr("Cannot calloc %zu * %zu = %zu bytes of memory.", |
783 | num, esize, num * esize); |
784 | |
785 | return (ptr); |
786 | } |
787 | |
788 | /* |
789 | * Strdup with error checking |
790 | */ |
791 | char * |
792 | xstrdup(const char *str) |
793 | { |
794 | size_t len = strlen(str) + 1; |
795 | char *nstr = xmalloc(len); |
796 | |
797 | return (memcpy(nstr, str, len)); |
798 | } |
799 | |
800 | /* |
801 | * Private version of basename() |
802 | */ |
803 | char * |
804 | xbasename(char *path) |
805 | { |
806 | char *cp; |
807 | |
808 | if ((cp = strrchr(path, '/')) != NULL((void *)0)) |
809 | return(cp+1); |
810 | else |
811 | return(path); |
812 | } |
813 | |
814 | /* |
815 | * Take a colon (':') separated path to a file and |
816 | * search until a component of that path is found and |
817 | * return the found file name. |
818 | */ |
819 | char * |
820 | searchpath(char *path) |
821 | { |
822 | char *file; |
823 | char *space; |
824 | int found; |
825 | struct stat statbuf; |
826 | |
827 | for (found = 0; !found && (file = strsep(&path, ":")) != NULL((void *)0); ) { |
828 | if ((space = strchr(file, ' ')) != NULL((void *)0)) |
829 | *space = CNULL'\0'; |
830 | found = stat(file, &statbuf) == 0; |
831 | if (space) |
832 | *space = ' '; /* Put back what we zapped */ |
833 | } |
834 | return (file); |
835 | } |