chat.c revision 37192
1/*- 2 * Copyright (c) 1998 Brian Somers <brian@Awfulhak.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * $Id: chat.c,v 1.49 1998/06/24 19:33:31 brian Exp $ 27 */ 28 29#include <sys/types.h> 30#include <netinet/in.h> 31#include <netinet/in_systm.h> 32#include <netinet/ip.h> 33#include <sys/un.h> 34 35#include <errno.h> 36#include <fcntl.h> 37#include <paths.h> 38#include <stdio.h> 39#include <stdlib.h> 40#include <string.h> 41#include <sys/wait.h> 42#include <termios.h> 43#include <unistd.h> 44 45#include "mbuf.h" 46#include "log.h" 47#include "defs.h" 48#include "timer.h" 49#include "lqr.h" 50#include "hdlc.h" 51#include "throughput.h" 52#include "fsm.h" 53#include "lcp.h" 54#include "ccp.h" 55#include "link.h" 56#include "async.h" 57#include "descriptor.h" 58#include "physical.h" 59#include "chat.h" 60#include "mp.h" 61#include "auth.h" 62#include "chap.h" 63#include "slcompress.h" 64#include "iplist.h" 65#include "ipcp.h" 66#include "filter.h" 67#include "datalink.h" 68#include "bundle.h" 69 70#define BUFLEFT(c) (sizeof (c)->buf - ((c)->bufend - (c)->buf)) 71#define issep(c) ((c) == '\t' || (c) == ' ') 72 73static void ExecStr(struct physical *, char *, char *, int); 74static char *ExpandString(struct chat *, const char *, char *, int, int); 75 76static void 77chat_PauseTimer(void *v) 78{ 79 struct chat *c = (struct chat *)v; 80 timer_Stop(&c->pause); 81 c->pause.load = 0; 82} 83 84static void 85chat_Pause(struct chat *c, u_long load) 86{ 87 timer_Stop(&c->pause); 88 c->pause.load += load; 89 c->pause.func = chat_PauseTimer; 90 c->pause.name = "chat pause"; 91 c->pause.arg = c; 92 timer_Start(&c->pause); 93} 94 95static void 96chat_TimeoutTimer(void *v) 97{ 98 struct chat *c = (struct chat *)v; 99 timer_Stop(&c->timeout); 100 c->TimedOut = 1; 101} 102 103static void 104chat_SetTimeout(struct chat *c) 105{ 106 timer_Stop(&c->timeout); 107 if (c->TimeoutSec > 0) { 108 c->timeout.load = SECTICKS * c->TimeoutSec; 109 c->timeout.func = chat_TimeoutTimer; 110 c->timeout.name = "chat timeout"; 111 c->timeout.arg = c; 112 timer_Start(&c->timeout); 113 } 114} 115 116static char * 117chat_NextChar(char *ptr, char ch) 118{ 119 for (; *ptr; ptr++) 120 if (*ptr == ch) 121 return ptr; 122 else if (*ptr == '\\') 123 if (*++ptr == '\0') 124 return NULL; 125 126 return NULL; 127} 128 129static int 130chat_UpdateSet(struct descriptor *d, fd_set *r, fd_set *w, fd_set *e, int *n) 131{ 132 struct chat *c = descriptor2chat(d); 133 int special, gotabort, gottimeout, needcr; 134 int TimedOut = c->TimedOut; 135 static char arg_term; /* An empty string */ 136 137 if (c->pause.state == TIMER_RUNNING) 138 return 0; 139 140 if (TimedOut) { 141 log_Printf(LogCHAT, "Expect timeout\n"); 142 if (c->nargptr == NULL) 143 c->state = CHAT_FAILED; 144 else { 145 /* c->state = CHAT_EXPECT; */ 146 c->argptr = &arg_term; 147 } 148 c->TimedOut = 0; 149 } 150 151 if (c->state != CHAT_EXPECT && c->state != CHAT_SEND) 152 return 0; 153 154 gottimeout = gotabort = 0; 155 156 if (c->arg < c->argc && (c->arg < 0 || *c->argptr == '\0')) { 157 /* Go get the next string */ 158 if (c->arg < 0 || c->state == CHAT_SEND) 159 c->state = CHAT_EXPECT; 160 else 161 c->state = CHAT_SEND; 162 163 special = 1; 164 while (special && (c->nargptr || c->arg < c->argc - 1)) { 165 if (c->arg < 0 || (!TimedOut && c->state == CHAT_SEND)) 166 c->nargptr = NULL; 167 168 if (c->nargptr != NULL) { 169 /* We're doing expect-send-expect.... */ 170 c->argptr = c->nargptr; 171 /* Put the '-' back in case we ever want to rerun our script */ 172 c->nargptr[-1] = '-'; 173 c->nargptr = chat_NextChar(c->nargptr, '-'); 174 if (c->nargptr != NULL) 175 *c->nargptr++ = '\0'; 176 } else { 177 int minus; 178 179 c->argptr = c->argv[++c->arg]; 180 181 if (c->state == CHAT_EXPECT) { 182 /* Look for expect-send-expect sequence */ 183 c->nargptr = c->argptr; 184 minus = 0; 185 while ((c->nargptr = chat_NextChar(c->nargptr, '-'))) { 186 c->nargptr++; 187 minus++; 188 } 189 190 if (minus % 2) 191 log_Printf(LogWARN, "chat_UpdateSet: \"%s\": Uneven number of" 192 " '-' chars, all ignored\n", c->argptr); 193 else if (minus) { 194 c->nargptr = chat_NextChar(c->argptr, '-'); 195 *c->nargptr++ = '\0'; 196 } 197 } 198 } 199 200 /* 201 * c->argptr now temporarily points into c->script (via c->argv) 202 * If it's an expect-send-expect sequence, we've just got the correct 203 * portion of that sequence. 204 */ 205 206 needcr = c->state == CHAT_SEND && *c->argptr != '!'; 207 208 /* We leave room for a potential HDLC header in the target string */ 209 ExpandString(c, c->argptr, c->exp + 2, sizeof c->exp - 2, needcr); 210 211 if (gotabort) { 212 if (c->abort.num < MAXABORTS) { 213 int len, i; 214 215 len = strlen(c->exp+2); 216 for (i = 0; i < c->abort.num; i++) 217 if (len > c->abort.string[i].len) { 218 int last; 219 220 for (last = c->abort.num; last > i; last--) { 221 c->abort.string[last].data = c->abort.string[last-1].data; 222 c->abort.string[last].len = c->abort.string[last-1].len; 223 } 224 break; 225 } 226 c->abort.string[i].len = len; 227 c->abort.string[i].data = (char *)malloc(len+1); 228 memcpy(c->abort.string[i].data, c->exp+2, len+1); 229 c->abort.num++; 230 } else 231 log_Printf(LogERROR, "chat_UpdateSet: too many abort strings\n"); 232 gotabort = 0; 233 } else if (gottimeout) { 234 c->TimeoutSec = atoi(c->exp + 2); 235 if (c->TimeoutSec <= 0) 236 c->TimeoutSec = 30; 237 gottimeout = 0; 238 } else if (c->nargptr == NULL && !strcmp(c->exp+2, "ABORT")) 239 gotabort = 1; 240 else if (c->nargptr == NULL && !strcmp(c->exp+2, "TIMEOUT")) 241 gottimeout = 1; 242 else { 243 if (c->exp[2] == '!') 244 ExecStr(c->physical, c->exp + 3, c->exp + 2, sizeof c->exp - 2); 245 246 if (c->exp[2] == '\0') { 247 /* Empty string, reparse (this may be better as a `goto start') */ 248 c->argptr = &arg_term; 249 return chat_UpdateSet(d, r, w, e, n); 250 } 251 252 special = 0; 253 } 254 } 255 256 if (special) { 257 if (gottimeout) 258 log_Printf(LogWARN, "chat_UpdateSet: TIMEOUT: Argument expected\n"); 259 else if (gotabort) 260 log_Printf(LogWARN, "chat_UpdateSet: ABORT: Argument expected\n"); 261 262 /* End of script - all ok */ 263 c->state = CHAT_DONE; 264 return 0; 265 } 266 267 /* set c->argptr to point in the right place */ 268 c->argptr = c->exp + 2; 269 c->arglen = strlen(c->argptr); 270 271 if (c->state == CHAT_EXPECT) { 272 /* We must check to see if the string's already been found ! */ 273 char *begin, *end; 274 275 end = c->bufend - c->arglen + 1; 276 if (end < c->bufstart) 277 end = c->bufstart; 278 for (begin = c->bufstart; begin < end; begin++) 279 if (!strncmp(begin, c->argptr, c->arglen)) { 280 c->bufstart = begin + c->arglen; 281 c->argptr += c->arglen; 282 c->arglen = 0; 283 /* Continue - we've already read our expect string */ 284 return chat_UpdateSet(d, r, w, e, n); 285 } 286 287 log_Printf(LogCHAT, "Expect(%d): %s\n", c->TimeoutSec, c->argptr); 288 chat_SetTimeout(c); 289 } 290 } 291 292 /* 293 * We now have c->argptr pointing at what we want to expect/send and 294 * c->state saying what we want to do... we now know what to put in 295 * the fd_set :-) 296 */ 297 298 if (c->state == CHAT_EXPECT) 299 return physical_UpdateSet(&c->physical->desc, r, NULL, e, n, 1); 300 else 301 return physical_UpdateSet(&c->physical->desc, NULL, w, e, n, 1); 302} 303 304static int 305chat_IsSet(struct descriptor *d, const fd_set *fdset) 306{ 307 struct chat *c = descriptor2chat(d); 308 return physical_IsSet(&c->physical->desc, fdset); 309} 310 311static void 312chat_UpdateLog(struct chat *c, int in) 313{ 314 if (log_IsKept(LogCHAT) || log_IsKept(LogCONNECT)) { 315 /* 316 * If a linefeed appears in the last `in' characters of `c's input 317 * buffer, output from there, all the way back to the last linefeed. 318 * This is called for every read of `in' bytes. 319 */ 320 char *ptr, *end, *stop, ch; 321 int level; 322 323 level = log_IsKept(LogCHAT) ? LogCHAT : LogCONNECT; 324 if (in == -1) 325 end = ptr = c->bufend; 326 else { 327 ptr = c->bufend - in; 328 for (end = c->bufend - 1; end >= ptr; end--) 329 if (*end == '\n') 330 break; 331 } 332 333 if (end >= ptr) { 334 for (ptr = c->bufend - (in == -1 ? 1 : in + 1); ptr >= c->bufstart; ptr--) 335 if (*ptr == '\n') 336 break; 337 ptr++; 338 stop = NULL; 339 while (stop < end) { 340 if ((stop = memchr(ptr, '\n', end - ptr)) == NULL) 341 stop = end; 342 ch = *stop; 343 *stop = '\0'; 344 if (level == LogCHAT || strstr(ptr, "CONNECT")) 345 log_Printf(level, "Received: %s\n", ptr); 346 *stop = ch; 347 ptr = stop + 1; 348 } 349 } 350 } 351} 352 353static void 354chat_Read(struct descriptor *d, struct bundle *bundle, const fd_set *fdset) 355{ 356 struct chat *c = descriptor2chat(d); 357 358 if (c->state == CHAT_EXPECT) { 359 ssize_t in; 360 char *abegin, *ebegin, *begin, *aend, *eend, *end; 361 int n; 362 363 /* 364 * XXX - should this read only 1 byte to guarantee that we don't 365 * swallow any ppp talk from the peer ? 366 */ 367 in = BUFLEFT(c); 368 if (in > sizeof c->buf / 2) 369 in = sizeof c->buf / 2; 370 371 in = physical_Read(c->physical, c->bufend, in); 372 if (in <= 0) 373 return; 374 375 /* `begin' and `end' delimit where we're going to strncmp() from */ 376 ebegin = c->bufend - c->arglen + 1; 377 eend = ebegin + in; 378 if (ebegin < c->bufstart) 379 ebegin = c->bufstart; 380 381 if (c->abort.num) { 382 abegin = c->bufend - c->abort.string[0].len + 1; 383 aend = c->bufend - c->abort.string[c->abort.num-1].len + in + 1; 384 if (abegin < c->bufstart) 385 abegin = c->bufstart; 386 } else { 387 abegin = ebegin; 388 aend = eend; 389 } 390 begin = abegin < ebegin ? abegin : ebegin; 391 end = aend < eend ? eend : aend; 392 393 c->bufend += in; 394 395 chat_UpdateLog(c, in); 396 397 if (c->bufend > c->buf + sizeof c->buf / 2) { 398 /* Shuffle our receive buffer back a bit */ 399 int chop; 400 401 for (chop = begin - c->buf; chop; chop--) 402 if (c->buf[chop] == '\n') 403 /* found some already-logged garbage to remove :-) */ 404 break; 405 406 if (!chop) 407 chop = begin - c->buf; 408 409 if (chop) { 410 char *from, *to; 411 412 to = c->buf; 413 from = to + chop; 414 while (from < c->bufend) 415 *to++ = *from++; 416 c->bufstart -= chop; 417 c->bufend -= chop; 418 begin -= chop; 419 end -= chop; 420 abegin -= chop; 421 aend -= chop; 422 ebegin -= chop; 423 eend -= chop; 424 } 425 } 426 427 for (; begin < end; begin++) 428 if (begin >= ebegin && begin < eend && 429 !strncmp(begin, c->argptr, c->arglen)) { 430 /* Got it ! */ 431 if (memchr(begin + c->arglen - 1, '\n', 432 c->bufend - begin - c->arglen + 1) == NULL) { 433 /* force it into the log */ 434 end = c->bufend; 435 c->bufend = begin + c->arglen; 436 chat_UpdateLog(c, -1); 437 c->bufend = end; 438 } 439 c->bufstart = begin + c->arglen; 440 c->argptr += c->arglen; 441 c->arglen = 0; 442 break; 443 } else if (begin >= abegin && begin < aend) { 444 for (n = c->abort.num - 1; n >= 0; n--) { 445 if (begin + c->abort.string[n].len > c->bufend) 446 break; 447 if (!strncmp(begin, c->abort.string[n].data, 448 c->abort.string[n].len)) { 449 if (memchr(begin + c->abort.string[n].len - 1, '\n', 450 c->bufend - begin - c->abort.string[n].len + 1) == NULL) { 451 /* force it into the log */ 452 end = c->bufend; 453 c->bufend = begin + c->abort.string[n].len; 454 chat_UpdateLog(c, -1); 455 c->bufend = end; 456 } 457 c->bufstart = begin + c->abort.string[n].len; 458 c->state = CHAT_FAILED; 459 return; 460 } 461 } 462 } 463 } 464} 465 466static int 467chat_Write(struct descriptor *d, struct bundle *bundle, const fd_set *fdset) 468{ 469 struct chat *c = descriptor2chat(d); 470 int result = 0; 471 472 if (c->state == CHAT_SEND) { 473 int wrote; 474 475 if (strstr(c->argv[c->arg], "\\P")) /* Don't log the password */ 476 log_Printf(LogCHAT, "Send: %s\n", c->argv[c->arg]); 477 else { 478 int sz; 479 480 sz = c->arglen - 1; 481 while (sz >= 0 && c->argptr[sz] == '\n') 482 sz--; 483 log_Printf(LogCHAT, "Send: %.*s\n", sz + 1, c->argptr); 484 } 485 486 if (physical_IsSync(c->physical)) { 487 /* There's always room for the HDLC header */ 488 c->argptr -= 2; 489 c->arglen += 2; 490 memcpy(c->argptr, "\377\003", 2); /* Prepend HDLC header */ 491 } 492 493 wrote = physical_Write(c->physical, c->argptr, c->arglen); 494 result = wrote ? 1 : 0; 495 if (wrote == -1) { 496 if (errno != EINTR) 497 log_Printf(LogERROR, "chat_Write: %s\n", strerror(errno)); 498 if (physical_IsSync(c->physical)) { 499 c->argptr += 2; 500 c->arglen -= 2; 501 } 502 } else if (wrote < 2 && physical_IsSync(c->physical)) { 503 /* Oops - didn't even write our HDLC header ! */ 504 c->argptr += 2; 505 c->arglen -= 2; 506 } else { 507 c->argptr += wrote; 508 c->arglen -= wrote; 509 } 510 } 511 512 return result; 513} 514 515void 516chat_Init(struct chat *c, struct physical *p, const char *data, int emptybuf, 517 const char *phone) 518{ 519 c->desc.type = CHAT_DESCRIPTOR; 520 c->desc.UpdateSet = chat_UpdateSet; 521 c->desc.IsSet = chat_IsSet; 522 c->desc.Read = chat_Read; 523 c->desc.Write = chat_Write; 524 c->physical = p; 525 526 c->state = CHAT_EXPECT; 527 528 if (data == NULL) { 529 *c->script = '\0'; 530 c->argc = 0; 531 } else { 532 strncpy(c->script, data, sizeof c->script - 1); 533 c->script[sizeof c->script - 1] = '\0'; 534 c->argc = MakeArgs(c->script, c->argv, VECSIZE(c->argv)); 535 } 536 537 c->arg = -1; 538 c->argptr = NULL; 539 c->nargptr = NULL; 540 541 if (emptybuf) 542 c->bufstart = c->bufend = c->buf; 543 544 c->TimeoutSec = 30; 545 c->TimedOut = 0; 546 c->phone = phone; 547 c->abort.num = 0; 548 549 memset(&c->pause, '\0', sizeof c->pause); 550 memset(&c->timeout, '\0', sizeof c->timeout); 551} 552 553void 554chat_Destroy(struct chat *c) 555{ 556 timer_Stop(&c->pause); 557 timer_Stop(&c->timeout); 558 while (c->abort.num) 559 free(c->abort.string[--c->abort.num].data); 560 c->abort.num = 0; 561} 562 563static char * 564findblank(char *p, int instring) 565{ 566 if (instring) { 567 while (*p) { 568 if (*p == '\\') { 569 strcpy(p, p + 1); 570 if (!*p) 571 break; 572 } else if (*p == '"') 573 return (p); 574 p++; 575 } 576 } else { 577 while (*p) { 578 if (issep(*p)) 579 return (p); 580 p++; 581 } 582 } 583 return p; 584} 585 586int 587MakeArgs(char *script, char **pvect, int maxargs) 588{ 589 int nargs, nb; 590 int instring; 591 592 nargs = 0; 593 while (*script) { 594 nb = strspn(script, " \t"); 595 script += nb; 596 if (*script) { 597 if (*script == '"') { 598 instring = 1; 599 script++; 600 if (*script == '\0') 601 break; /* Shouldn't return here. Need to null 602 * terminate below */ 603 } else 604 instring = 0; 605 if (nargs >= maxargs - 1) 606 break; 607 *pvect++ = script; 608 nargs++; 609 script = findblank(script, instring); 610 if (*script) 611 *script++ = '\0'; 612 } 613 } 614 *pvect = NULL; 615 return nargs; 616} 617 618/* 619 * \c don't add a cr 620 * \d Sleep a little (delay 2 seconds 621 * \n Line feed character 622 * \P Auth Key password 623 * \p pause 0.25 sec 624 * \r Carrige return character 625 * \s Space character 626 * \T Telephone number(s) (defined via `set phone') 627 * \t Tab character 628 * \U Auth User 629 */ 630static char * 631ExpandString(struct chat *c, const char *str, char *result, int reslen, 632 int sendmode) 633{ 634 int addcr = 0; 635 636 result[--reslen] = '\0'; 637 if (sendmode) 638 addcr = 1; 639 while (*str && reslen > 0) { 640 switch (*str) { 641 case '\\': 642 str++; 643 switch (*str) { 644 case 'c': 645 if (sendmode) 646 addcr = 0; 647 break; 648 case 'd': /* Delay 2 seconds */ 649 chat_Pause(c, 2 * SECTICKS); 650 break; 651 case 'p': 652 chat_Pause(c, SECTICKS / 4); 653 break; /* Pause 0.25 sec */ 654 case 'n': 655 *result++ = '\n'; 656 reslen--; 657 break; 658 case 'r': 659 *result++ = '\r'; 660 reslen--; 661 break; 662 case 's': 663 *result++ = ' '; 664 reslen--; 665 break; 666 case 't': 667 *result++ = '\t'; 668 reslen--; 669 break; 670 case 'P': 671 strncpy(result, c->physical->dl->bundle->cfg.auth.key, reslen); 672 reslen -= strlen(result); 673 result += strlen(result); 674 break; 675 case 'T': 676 strncpy(result, c->phone, reslen); 677 reslen -= strlen(result); 678 result += strlen(result); 679 break; 680 case 'U': 681 strncpy(result, c->physical->dl->bundle->cfg.auth.name, reslen); 682 reslen -= strlen(result); 683 result += strlen(result); 684 break; 685 default: 686 reslen--; 687 *result++ = *str; 688 break; 689 } 690 if (*str) 691 str++; 692 break; 693 case '^': 694 str++; 695 if (*str) { 696 *result++ = *str++ & 0x1f; 697 reslen--; 698 } 699 break; 700 default: 701 *result++ = *str++; 702 reslen--; 703 break; 704 } 705 } 706 if (--reslen > 0) { 707 if (addcr) 708 *result++ = '\r'; 709 } 710 if (--reslen > 0) 711 *result++ = '\0'; 712 return (result); 713} 714 715static void 716ExecStr(struct physical *physical, char *command, char *out, int olen) 717{ 718 pid_t pid; 719 int fids[2]; 720 char *vector[MAXARGS], *startout, *endout; 721 int stat, nb; 722 723 log_Printf(LogCHAT, "Exec: %s\n", command); 724 MakeArgs(command, vector, VECSIZE(vector)); 725 726 if (pipe(fids) < 0) { 727 log_Printf(LogCHAT, "Unable to create pipe in ExecStr: %s\n", 728 strerror(errno)); 729 *out = '\0'; 730 return; 731 } 732 if ((pid = fork()) == 0) { 733 close(fids[0]); 734 timer_TermService(); 735 fids[1] = fcntl(fids[1], F_DUPFD, 4); 736 dup2(physical_GetFD(physical), STDIN_FILENO); 737 dup2(STDIN_FILENO, STDOUT_FILENO); 738 dup2(fids[1], STDERR_FILENO); 739 close(3); 740 if (open(_PATH_TTY, O_RDWR) == 3) 741 fcntl(3, F_SETFD, 0); /* Clear close-on-exec flag */ 742 else 743 fcntl(3, F_SETFD, 1); /* Set close-on-exec flag */ 744 setuid(geteuid()); 745 execvp(vector[0], vector); 746 fprintf(stderr, "execvp failed: %s: %s\n", vector[0], strerror(errno)); 747 exit(127); 748 } else { 749 char *name = strdup(vector[0]); 750 751 close(fids[1]); 752 endout = out + olen - 1; 753 startout = out; 754 while (out < endout) { 755 nb = read(fids[0], out, 1); 756 if (nb <= 0) 757 break; 758 out++; 759 } 760 *out = '\0'; 761 close(fids[0]); 762 close(fids[1]); 763 waitpid(pid, &stat, WNOHANG); 764 if (WIFSIGNALED(stat)) { 765 log_Printf(LogWARN, "%s: signal %d\n", name, WTERMSIG(stat)); 766 free(name); 767 *out = '\0'; 768 return; 769 } else if (WIFEXITED(stat)) { 770 switch (WEXITSTATUS(stat)) { 771 case 0: 772 free(name); 773 break; 774 case 127: 775 log_Printf(LogWARN, "%s: %s\n", name, startout); 776 free(name); 777 *out = '\0'; 778 return; 779 break; 780 default: 781 log_Printf(LogWARN, "%s: exit %d\n", name, WEXITSTATUS(stat)); 782 free(name); 783 *out = '\0'; 784 return; 785 break; 786 } 787 } else { 788 log_Printf(LogWARN, "%s: Unexpected exit result\n", name); 789 free(name); 790 *out = '\0'; 791 return; 792 } 793 } 794} 795