source: trunk/src/sh_extern.c@ 233

Last change on this file since 233 was 227, checked in by katerina, 16 years ago

Fix warnings with -fstack-check

File size: 34.3 KB
Line 
1/* SAMHAIN file system integrity testing */
2/* Copyright (C) 2000,2004 Rainer Wichmann */
3/* */
4/* This program is free software; you can redistribute it */
5/* and/or modify */
6/* it under the terms of the GNU General Public License as */
7/* published by */
8/* the Free Software Foundation; either version 2 of the License, or */
9/* (at your option) any later version. */
10/* */
11/* This program is distributed in the hope that it will be useful, */
12/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
13/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
14/* GNU General Public License for more details. */
15/* */
16/* You should have received a copy of the GNU General Public License */
17/* along with this program; if not, write to the Free Software */
18/* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
19
20
21#include "config_xor.h"
22
23
24#include <stdio.h>
25#include <string.h>
26#ifdef HAVE_MEMORY_H
27#include <memory.h>
28#endif
29
30/* replace #if 0 by #if 1 and set an appropriate path in front of '/pdbg.'
31 * for debugging
32 */
33#if 0
34#define PDGBFILE "/pdbg."
35#endif
36
37
38#if defined(PDGBFILE)
39static FILE * pdbg = NULL;
40static FILE * pdbgc = NULL;
41#define PDBG_OPEN if (pdbg == NULL) pdbg = fopen(PDGBFILE"main", "a")
42#define PDBG_CLOSE fclose (pdbg); pdbg = NULL
43#define PDBG(arg) fprintf(pdbg, "PDBG: step %d\n", arg); fflush(pdbg)
44#define PDBG_D(arg) fprintf(pdbg, "PDBG: %d\n", arg); fflush(pdbg)
45#define PDBG_S(arg) fprintf(pdbg, "PDBG: %s\n", arg); fflush(pdbg)
46
47#define PDBGC_OPEN if (pdbgc == NULL) pdbgc = fopen(PDGBFILE"child", "a")
48#define PDBGC_CLOSE fclose (pdbgc); pdbgc = NULL
49#define PDBGC(arg) fprintf(pdbgc, "PDBGC: step %d\n", arg); fflush(pdbgc)
50#define PDBGC_D(arg) fprintf(pdbgc, "PDBGC: %d\n", arg); fflush(pdbgc)
51#define PDBGC_S(arg) fprintf(pdbgc, "PDBGC: %s\n", arg); fflush(pdbgc)
52#else
53#define PDBG_OPEN
54#define PDBG_CLOSE
55#define PDBG(arg)
56#define PDBG_D(arg)
57#define PDBG_S(arg)
58#define PDBGC_OPEN
59#define PDBGC_CLOSE
60#define PDBGC(arg)
61#define PDBGC_D(arg)
62#define PDBGC_S(arg)
63#endif
64
65
66#include <stdlib.h>
67#include <pwd.h>
68#include <unistd.h>
69#include <fcntl.h>
70#include <signal.h>
71#include <sys/stat.h>
72#include <sys/types.h>
73#include <errno.h>
74#include <sys/wait.h>
75
76#if TIME_WITH_SYS_TIME
77#include <sys/time.h>
78#include <time.h>
79#else
80#if HAVE_SYS_TIME_H
81#include <sys/time.h>
82#else
83#include <time.h>
84#endif
85#endif
86
87
88#include "samhain.h"
89#include "sh_utils.h"
90#include "sh_unix.h"
91#include "sh_tiger.h"
92#include "sh_extern.h"
93#include "sh_calls.h"
94#include "sh_filter.h"
95#define SH_NEED_PWD_GRP 1
96#include "sh_static.h"
97
98
99#undef FIL__
100#define FIL__ _("sh_extern.c")
101
102extern int get_the_fd (SL_TICKET ticket);
103
104/*
105 * -- generic safe popen
106 */
107
108int sh_ext_popen (sh_tas_t * task)
109{
110 long status = 0;
111 int flags;
112 char * tmp;
113 char * tmp2;
114 int errnum;
115 int pipedes[2];
116 FILE * outf = NULL;
117 char * envp[1];
118 char * argp[2];
119
120 char * errfile;
121 char errbuf[SH_ERRBUF_SIZE];
122
123 static int some_error = 0;
124
125#if defined (__linux__)
126 SL_TICKET fd = -1;
127 char pname[128];
128 int pfd = -1;
129#endif
130
131 SL_ENTER(_("sh_ext_popen"));
132
133 /* Linux, HP-UX and FreeBSD will happily accept envp = argp = NULL
134 * Solaris (and probably some other Unices)
135 * needs a valid *envp[] with envp[0] = NULL;
136 * and similarly for argp
137 * OpenBSD finally needs non-null argp[0] ...
138 */
139 argp[0] = task->command;
140 argp[1] = NULL;
141 envp[0] = NULL;
142
143 /*
144 * -- check whether path is trustworthy
145 */
146 status = sl_trustfile(task->command, NULL, NULL);
147#if 0
148 if ((uid_t) -1 != task->trusted_users[0])
149 {
150 status = sl_trustfile(task->command, task->trusted_users, NULL);
151 }
152#endif
153
154 PDBG_OPEN;
155 PDBG_D( (int) status);
156
157 if ( SL_ENONE != status)
158 {
159 PDBG_S("SL_ENONE != status");
160 if (some_error == 0)
161 {
162 tmp = sh_util_safe_name (task->command);
163 errfile = sl_trust_errfile();
164 if (errfile[0] != '\0')
165 {
166 tmp2 = sh_util_safe_name (sl_trust_errfile());
167 sh_error_handle((-1), FIL__, __LINE__, status, MSG_E_TRUST2,
168 sl_error_string((int)status), tmp, tmp2);
169 SH_FREE(tmp2);
170 }
171 else
172 {
173 sh_error_handle((-1), FIL__, __LINE__, status, MSG_E_TRUST1,
174 sl_error_string((int)status), tmp);
175 }
176 SH_FREE(tmp);
177 }
178 some_error = 1;
179 SL_RETURN ((-1), _("sh_ext_popen"));
180 }
181
182 PDBG(1);
183
184 /*
185 * -- check whether the checksum is correct; with linux emulate fdexec
186 */
187#if !defined(__linux__) && !defined(SL_DEBUG)
188 if (task->checksum[0] != '\0')
189 {
190 char hashbuf[KEYBUF_SIZE];
191 PDBG_S("checksum test");
192 if (0 != sl_strcmp(task->checksum,
193 sh_tiger_hash (task->command, TIGER_FILE, TIGER_NOLIM,
194 hashbuf, sizeof(hashbuf))
195 )
196 )
197 {
198 PDBG_S("checksum mismatch");
199 if (some_error == 0)
200 {
201 tmp = sh_util_safe_name (task->command);
202 sh_error_handle((-1), FIL__, __LINE__, 0, MSG_E_HASH, tmp);
203 SH_FREE(tmp);
204 }
205 some_error = 1;
206 SL_RETURN ((-1), _("sh_ext_popen"));
207 }
208 }
209#endif
210
211 some_error = 0;
212
213 PDBG(2);
214
215 /*
216 * -- Create the pipe
217 */
218 if (aud_pipe(FIL__, __LINE__, pipedes) < 0)
219 {
220 PDBG_S("pipe() failure");
221 errnum = errno;
222 sh_error_handle((-1), FIL__, __LINE__, errnum, MSG_E_SUBGEN,
223 sh_error_message(errnum, errbuf, sizeof(errbuf)), _("pipe"));
224 SL_RETURN ((-1), _("sh_ext_popen"));
225 }
226
227 PDBG(3);
228
229 /*
230 * -- Flush streams and fork
231 */
232 fflush (NULL);
233
234 task->pid = aud_fork(FIL__, __LINE__);
235
236 if (task->pid == (pid_t) - 1)
237 {
238 PDBG_S("fork() failure");
239 /*@-usedef@*/
240 (void) close(pipedes[0]);
241 (void) close(pipedes[1]);
242 /*@+usedef@*/
243 errnum = errno;
244 sh_error_handle((-1), FIL__, __LINE__, errnum, MSG_E_SUBGEN,
245 sh_error_message(errnum, errbuf, sizeof(errbuf)), _("fork"));
246 SL_RETURN ((-1), _("sh_ext_popen"));
247 }
248
249 PDBG(4);
250
251 if (task->pid == (pid_t) 0)
252 {
253 /*
254 * -- fork again, if requested
255 */
256 if (S_TRUE == task->fork_twice)
257 {
258 task->pid = aud_fork(FIL__, __LINE__);
259
260 if (task->pid == (pid_t) - 1)
261 {
262 aud__exit (FIL__, __LINE__, EXIT_FAILURE);
263 }
264 }
265
266 if (task->pid == (pid_t) 0)
267 {
268 PDBGC_OPEN;
269 PDBGC(1);
270
271 /*
272 * -- grandchild - make write side of the pipe stdin
273 */
274 if (task->rw == 'w')
275 {
276 if (retry_aud_dup2(FIL__, __LINE__,
277 pipedes[STDIN_FILENO], STDIN_FILENO) < 0)
278 aud__exit(FIL__, __LINE__,EXIT_FAILURE);
279 }
280 else
281 {
282 if (retry_aud_dup2(FIL__, __LINE__,
283 pipedes[STDOUT_FILENO], STDOUT_FILENO) < 0)
284 aud__exit(FIL__, __LINE__,EXIT_FAILURE);
285 }
286 PDBGC(2);
287
288
289 /* close the pipe descriptors
290 */
291 (void) close (pipedes[STDIN_FILENO]);
292 (void) close (pipedes[STDOUT_FILENO]);
293
294 /* don't leak file descriptors
295 */
296#if !defined(PDGBFILE)
297 sh_unix_closeall (3, task->com_fd, SL_TRUE); /* in child process */
298#endif
299
300 /* drop root privileges, if possible && requested
301 */
302 if (task->privileged == 0 && 0 == getuid())
303 {
304 PDBGC_S("privileged");
305
306 /* zero priv info
307 */
308 memset(skey, 0, sizeof(sh_key_t));
309
310 (void) aud_setgid(FIL__, __LINE__,(gid_t) task->run_user_gid);
311 (void) aud_setuid(FIL__, __LINE__,(uid_t) task->run_user_uid);
312 /* make sure we cannot get root again
313 */
314 if (aud_setuid(FIL__, __LINE__,0) >= 0)
315 aud__exit(FIL__, __LINE__,EXIT_FAILURE);
316 }
317
318 PDBGC(3);
319 (void) fflush(NULL);
320
321 if (task->rw == 'w')
322 {
323 PDBGC_S("w");
324 (void) fcntl (STDOUT_FILENO, F_SETFD, FD_CLOEXEC);
325 (void) fcntl (STDERR_FILENO, F_SETFD, FD_CLOEXEC);
326 /*
327 freopen(_("/dev/null"), "r+", stderr);
328 freopen(_("/dev/null"), "r+", stdout);
329 */
330 }
331 else
332 {
333 PDBGC_S("r");
334 (void) retry_aud_dup2 (FIL__, __LINE__,
335 STDOUT_FILENO, STDERR_FILENO);
336 (void) fcntl (STDIN_FILENO, F_SETFD, FD_CLOEXEC);
337 /*
338 freopen(_("/dev/null"), "r+", stdin);
339 */
340 }
341
342 PDBGC(4);
343
344
345#if defined(__linux__)
346 /*
347 * -- emulate an fdexec with checksum testing
348 */
349 if (task->checksum[0] != '\0')
350 {
351 PDBGC_S("fexecve");
352 if (task->com_fd != (-1))
353 {
354 pfd = retry_aud_dup(FIL__, __LINE__, task->com_fd);
355 if (pfd < 0)
356 {
357 PDBGC_S("fexecve: dup2 failed");
358 aud__exit(FIL__, __LINE__, EXIT_FAILURE);
359 }
360 }
361 else
362 {
363 char hashbuf[KEYBUF_SIZE];
364
365 fd =
366 sl_open_read(task->command,
367 task->privileged==0 ? SL_NOPRIV : SL_YESPRIV);
368
369 if (0 != sl_strcmp(task->checksum,
370 sh_tiger_hash (task->command,
371 fd, TIGER_NOLIM, hashbuf, sizeof(hashbuf))))
372 {
373 PDBGC_S("fexecve: checksum mismatch");
374 aud__exit(FIL__, __LINE__, EXIT_FAILURE);
375 }
376 pfd = get_the_fd(fd);
377 }
378
379 PDBGC(5);
380 sl_snprintf(pname, sizeof(pname), _("/proc/self/fd/%d"), pfd);
381 if (access(pname, R_OK|X_OK) == 0) /* flawfinder: ignore */
382 {
383 PDBGC(6);
384 PDBGC_CLOSE;
385 fcntl (pfd, F_SETFD, FD_CLOEXEC);
386 retry_aud_execve (FIL__, __LINE__,
387 pname,
388 (task->argc == 0) ? NULL : task->argv,
389 (task->envc == 0) ? NULL : task->envv
390 );
391
392 errnum = errno;
393 PDBGC_OPEN;
394 PDBGC_S(strerror(errnum));
395 PDBGC_S(task->command);
396 PDBGC_S("fexecve: failed");
397 PDBGC_CLOSE;
398 /* failed
399 */
400 aud__exit(FIL__, __LINE__, EXIT_FAILURE);
401 }
402 PDBGC_S("fexecve: not working");
403 /*
404 * procfs not working, go ahead; checksum is tested already
405 */
406 if (fd != -1)
407 sl_close(fd);
408 else if (pfd != -1)
409 close(fd);
410 }
411#endif
412
413 PDBGC_S(" -- non fexecve --");
414 /*
415 * -- execute path if executable
416 */
417 if (0 == access(task->command, R_OK|X_OK)) /* flawfinder: ignore */
418 {
419 PDBGC(5);
420 PDBGC_CLOSE;
421 (void) retry_aud_execve (FIL__, __LINE__,
422 task->command,
423 (task->argc == 0) ? argp : task->argv,
424 (task->envc == 0) ? envp : task->envv
425 );
426 }
427 errnum = errno;
428 PDBGC_OPEN;
429 PDBGC_S(strerror(errnum));
430 PDBGC_S(task->command);
431 PDBGC_S("execve: failed");
432 PDBGC_CLOSE;
433 /* failed
434 */
435 aud__exit(FIL__, __LINE__, EXIT_FAILURE);
436 }
437 /*
438 * if we have forked twice, this is parent::detached_subprocess
439 */
440 if (S_TRUE == task->fork_twice)
441 {
442 aud__exit (FIL__, __LINE__, 0);
443 }
444 }
445
446
447 /*
448 * -- parent; task->pid is child pid; exit status is status of
449 * grandchild if exited
450 */
451 if (S_TRUE == task->fork_twice)
452 {
453 (void) waitpid (task->pid, NULL, 0);
454 }
455
456 PDBG(5);
457 /* open an output stream on top of the write side of the pipe
458 */
459 if (task->rw == 'w')
460 {
461 PDBG_S("is w");
462 (void) close (pipedes[STDIN_FILENO]);
463 (void) retry_fcntl (FIL__, __LINE__, pipedes[STDOUT_FILENO],
464 F_SETFD, FD_CLOEXEC);
465 outf = fdopen (pipedes[STDOUT_FILENO], "w");
466 }
467 else
468 {
469 PDBG_S("is r");
470 (void) close (pipedes[STDOUT_FILENO]);
471 (void) retry_fcntl (FIL__, __LINE__, pipedes[STDIN_FILENO],
472 F_SETFD, FD_CLOEXEC);
473 outf = fdopen (pipedes[STDIN_FILENO], "r");
474 }
475
476 if (outf == NULL)
477 {
478 errnum = errno;
479 PDBG_S("outf == NULL");
480 tmp = sh_util_safe_name (task->command);
481
482 if (task->privileged == 0 && 0 == getuid())
483 sh_error_handle((-1), FIL__, __LINE__, errnum, MSG_NOEXEC,
484 (UID_CAST) task->run_user_uid, tmp);
485 else
486 sh_error_handle((-1), FIL__, __LINE__, errnum, MSG_NOEXEC,
487 (UID_CAST) getuid(), tmp);
488
489 SH_FREE(tmp);
490
491 (void) aud_kill (FIL__, __LINE__, task->pid, SIGKILL);
492 (void) close (pipedes[STDOUT_FILENO]);
493 (void) close (pipedes[STDIN_FILENO]);
494 (void) waitpid (task->pid, NULL, 0);
495 task->pid = 0;
496
497 SL_RETURN ((-1), _("sh_ext_popen"));
498 }
499
500 if (task->rw == 'w')
501 task->pipeFD = pipedes[STDOUT_FILENO];
502 else
503 task->pipeFD = pipedes[STDIN_FILENO];
504
505 PDBG_D(task->pipeFD);
506
507 task->pipeTI = sl_make_ticket(task->pipeFD, _("pipe"));
508
509 flags = (int) retry_fcntl (FIL__, __LINE__, task->pipeFD, F_GETFL, 0);
510 if (flags != (-1))
511 (void) retry_fcntl (FIL__, __LINE__, task->pipeFD,
512 F_SETFL, flags|O_NONBLOCK);
513 task->pipe = outf;
514
515 PDBG_S("return from popen");
516 PDBG_CLOSE;
517
518 SL_RETURN (0, _("sh_ext_popen"));
519}
520
521/*
522 * -- close the pipe
523 */
524extern int flag_err_debug;
525
526int sh_ext_pclose (sh_tas_t * task)
527{
528 int status = 0;
529 int retry = 0;
530 pid_t retval;
531 char infomsg[256];
532
533 SL_ENTER(_("sh_ext_pclose"));
534
535 PDBG_OPEN;
536 PDBG_S(" -> pclose");
537 (void) fflush(task->pipe);
538 (void) fclose(task->pipe);
539 if (!SL_ISERROR(task->pipeTI))
540 (void) sl_close(task->pipeTI);
541
542 task->pipe = NULL;
543 task->pipeFD = (-1);
544 task->pipeTI = SL_ETICKET;
545
546 if (S_FALSE == task->fork_twice)
547 {
548 infomsg[0] = '\0';
549
550 nochmal:
551 retval = waitpid(task->pid, &(task->exit_status), WNOHANG|WUNTRACED);
552 /*@-bufferoverflowhigh@*/
553 if (task->pid == retval)
554 {
555#ifndef USE_UNO
556 if (WIFEXITED(task->exit_status) != 0)
557 {
558 task->exit_status = WEXITSTATUS(task->exit_status);
559 if ((flag_err_debug == SL_TRUE) || (task->exit_status != 0))
560 sl_snprintf(infomsg, sizeof(infomsg),
561 _("Subprocess exited normally with status %d"),
562 task->exit_status);
563 }
564 else if (WIFSIGNALED(task->exit_status) != 0)
565 {
566 sl_snprintf(infomsg, sizeof(infomsg),
567 _("Subprocess terminated by signal %d"),
568 WTERMSIG(task->exit_status));
569 task->exit_status = EXIT_FAILURE;
570 }
571 else if (WIFSTOPPED(task->exit_status) != 0)
572 {
573 sl_snprintf(infomsg, sizeof(infomsg),
574 _("Subprocess stopped by signal %d, killing"),
575 WSTOPSIG(task->exit_status));
576 task->exit_status = EXIT_FAILURE;
577 (void) aud_kill (FIL__, __LINE__, task->pid, 9);
578 (void) retry_msleep (0, 30);
579 (void) waitpid (task->pid, NULL, WNOHANG|WUNTRACED);
580 }
581 else
582 {
583 sl_snprintf(infomsg, sizeof(infomsg),
584 _("Subprocess exit status unknown"));
585 task->exit_status = EXIT_FAILURE;
586 }
587#else
588 task->exit_status = EXIT_FAILURE;
589#endif
590 }
591 else if (0 == retval)
592 {
593 if (retry < 3)
594 {
595 ++retry;
596 (void) retry_msleep(0, (retry * 30));
597 goto nochmal;
598 }
599 (void) aud_kill (FIL__, __LINE__, task->pid, 9);
600 sl_snprintf(infomsg, sizeof(infomsg),
601 _("Subprocess not yet exited, killing"));
602 task->exit_status = EXIT_FAILURE;
603 (void) waitpid (task->pid, NULL, 0);
604 }
605 else
606 {
607 sl_snprintf(infomsg, sizeof(infomsg),
608 _("Waitpid returned error %d\n"), errno);
609 task->exit_status = EXIT_FAILURE;
610 }
611 /*@+bufferoverflowhigh@*/
612 status = task->exit_status;
613 if (flag_err_debug == SL_TRUE)
614 {
615 sh_error_handle(SH_ERR_ALL, FIL__, __LINE__, task->exit_status,
616 MSG_E_SUBGEN, infomsg, _("sh_ext_pclose"));
617 }
618 else if (status != 0)
619 {
620 sh_error_handle(SH_ERR_INFO, FIL__, __LINE__, task->exit_status,
621 MSG_E_SUBGEN, infomsg, _("sh_ext_pclose"));
622 }
623 }
624
625 task->pid = 0;
626 task->exit_status = 0;
627 PDBG_S(" <--");
628 PDBG_CLOSE;
629 SL_RETURN (status, _("sh_ext_pclose"));
630}
631
632void sh_ext_tas_init (sh_tas_t * tas)
633{
634 int i;
635
636 tas->command = NULL;
637 tas->argc = 0;
638 tas->envc = 0;
639 tas->checksum[0] = '\0';
640 tas->pipeFD = (-1);
641 tas->pipeTI = SL_ETICKET;
642 tas->pid = (pid_t) -1;
643 tas->privileged = 1;
644 tas->pipe = NULL;
645 tas->rw = 'w';
646 tas->exit_status = 0;
647 tas->fork_twice = S_TRUE;
648
649 for (i = 0; i < 32; ++i)
650 {
651 tas->argv[i] = NULL;
652 tas->envv[i] = NULL;
653#if 0
654 tas->trusted_users[i] = (uid_t) -1;
655#endif
656 }
657
658 tas->run_user_uid = (uid_t) getuid();
659 tas->run_user_gid = (gid_t) getgid();
660
661 tas->com_fd = -1;
662 tas->com_ti = -1;
663 return;
664}
665
666
667int sh_ext_tas_add_envv(sh_tas_t * tas, const char * key, const char * val)
668{
669 size_t sk = 0, sv = 0;
670 int si;
671
672 SL_ENTER(_("sh_ext_tas_add_envv"));
673
674 if (tas == NULL || (key == NULL && val == NULL) ||
675 tas->envc >= 30)
676 {
677 SL_RETURN (-1, _("sh_ext_tas_add_envv"));
678 }
679 if (key != NULL)
680 sk = strlen(key) + 1;
681 if (val != NULL)
682 sv = strlen(val) + 1;
683
684 if (!sl_ok_adds(sk, sv))
685 {
686 SL_RETURN (-1, _("sh_ext_tas_add_envv"));
687 }
688 si = tas->envc;
689 tas->envv[si] = SH_ALLOC(sk + sv);
690
691 if (key != NULL)
692 {
693 (void) sl_strlcpy(tas->envv[si], key, sk+sv);
694 (void) sl_strlcat(tas->envv[si], "=", sk+sv);
695 if (val != NULL)
696 (void) sl_strlcat(tas->envv[si], val, sk+sv);
697 }
698 else
699 (void) sl_strlcpy(tas->envv[si], val, sv);
700
701 ++(tas->envc);
702 SL_RETURN ((tas->envc), _("sh_ext_tas_add_envv"));
703}
704
705int sh_ext_tas_rm_argv(sh_tas_t * tas)
706{
707 int last;
708
709 SL_ENTER(_("sh_ext_tas_rm_argv"));
710 if (tas == NULL || tas->argc == 0)
711 {
712 SL_RETURN (-1, _("sh_ext_tas_rm_argv"));
713 }
714
715 last = (tas->argc - 1);
716 --(tas->argc);
717 SH_FREE(tas->argv[last]);
718 tas->argv[last] = NULL;
719 SL_RETURN ((tas->argc), _("sh_ext_tas_rm_argv"));
720}
721
722int sh_ext_tas_add_argv(sh_tas_t * tas, const char * val)
723{
724 size_t sv = 0;
725 int si;
726
727 SL_ENTER(_("sh_ext_tas_add_argv"));
728
729 if (tas == NULL || val == NULL ||
730 tas->argc >= 30)
731 {
732 SL_RETURN (-1, _("sh_ext_tas_add_argv"));
733 }
734
735 if (val != NULL)
736 sv = strlen(val) + 1;
737
738 si = tas->argc;
739 tas->argv[si] = SH_ALLOC(sv);
740
741 (void) sl_strlcpy(tas->argv[si], val, sv);
742
743 ++(tas->argc);
744 SL_RETURN ((tas->argc), _("sh_ext_tas_add_argv"));
745}
746
747void sh_ext_tas_command(sh_tas_t * tas, const char * command)
748{
749 size_t len = sl_strlen(command);
750 tas->command = SH_ALLOC(len+1);
751 (void) sl_strlcpy(tas->command, command, len+1);
752 return;
753}
754
755void sh_ext_tas_free(sh_tas_t * tas)
756{
757 int i;
758 if (NULL != tas->command) SH_FREE(tas->command);
759
760 for (i = 0; i < 32; ++i)
761 {
762 if (NULL != tas->argv[i]) SH_FREE(tas->argv[i]);
763 if (NULL != tas->envv[i]) SH_FREE(tas->envv[i]);
764 }
765
766 if (tas->com_ti != (-1))
767 {
768 (void) sl_close(tas->com_ti);
769 tas->com_ti = -1;
770 tas->com_fd = -1;
771 }
772
773 return;
774}
775
776/* Execute command, return first line of output
777 * ifconfig | grep -1 lo | tail -n 1 | sed s/.*inet addr:\([0-9.]*\)\(.*\)/\1/
778 */
779char * sh_ext_popen_str (char * command)
780{
781 sh_tas_t task;
782 struct sigaction new_act;
783 struct sigaction old_act;
784 char * out = NULL;
785 int status;
786
787 SL_ENTER(_("sh_ext_popen_str"));
788
789 sh_ext_tas_init(&task);
790
791 (void) sh_ext_tas_add_envv (&task, _("SHELL"),
792 _("/bin/sh"));
793 (void) sh_ext_tas_add_envv (&task, _("PATH"),
794 _("/sbin:/bin:/usr/sbin:/usr/bin:/usr/ucb"));
795 (void) sh_ext_tas_add_envv (&task, _("IFS"), " \n\t");
796 if (sh.timezone != NULL)
797 {
798 (void) sh_ext_tas_add_envv(&task, "TZ", sh.timezone);
799 }
800
801 sh_ext_tas_command(&task, _("/bin/sh"));
802
803 (void) sh_ext_tas_add_argv(&task, _("/bin/sh"));
804 (void) sh_ext_tas_add_argv(&task, _("-c"));
805 (void) sh_ext_tas_add_argv(&task, command);
806
807 task.rw = 'r';
808 task.fork_twice = S_FALSE;
809
810 status = sh_ext_popen(&task);
811
812 if (status != 0)
813 {
814 sh_error_handle(SH_ERR_ALL, FIL__, __LINE__, status, MSG_E_SUBGEN,
815 _("Could not open pipe"), _("sh_ext_popen_str"));
816 SL_RETURN ((NULL), _("sh_ext_popen_str"));
817 }
818
819 /* ignore SIGPIPE (instead get EPIPE if connection is closed)
820 */
821 new_act.sa_handler = SIG_IGN;
822 (void) retry_sigaction (FIL__, __LINE__, SIGPIPE, &new_act, &old_act);
823
824 /* read from the open pipe
825 */
826 if (task.pipe != NULL)
827 {
828 int try = 1200; /* 1000 * 0.1 = 120 sec */
829 sh_string * s = sh_string_new(0);
830 do {
831 sh_string_read(s, task.pipe, 0);
832 if (sh_string_len(s) == 0)
833 {
834 --try; retry_msleep(0, 100);
835 }
836 } while (sh_string_len(s) == 0 && try != 0);
837
838 if (sh_string_len(s) == 0)
839 {
840 sh_error_handle(SH_ERR_ALL, FIL__, __LINE__, status, MSG_E_SUBGEN,
841 _("No output from command"), _("sh_ext_popen_str"));
842 }
843
844 out = sh_util_strdup(sh_string_str(s));
845 sh_string_destroy(&s);
846 }
847
848 /* restore old signal handler
849 */
850 (void) retry_sigaction (FIL__, __LINE__, SIGPIPE, &old_act, NULL);
851
852 /* close pipe and return exit status
853 */
854 (void) sh_ext_pclose(&task);
855 sh_ext_tas_free (&task);
856 SL_RETURN ((out), _("sh_ext_popen_str"));
857}
858
859
860
861
862/* --------------- EXTERN STUFF ------------------- */
863
864#if defined(WITH_EXTERNAL)
865
866typedef struct _sh_com_t
867{
868 char type[4];
869
870 sh_filter_type * filter;
871
872 time_t deadtime;
873 time_t last_run;
874
875 sh_tas_t tas;
876
877 struct _sh_com_t * next;
878
879} sh_com_t;
880
881
882static
883void set3 (char * pos, char c1, char c2, char c3)
884{
885 pos[0] = c1;
886 pos[1] = c2;
887 pos[2] = c3;
888 pos[3] = '\0';
889 return;
890}
891
892
893
894/* initialize the external command structure
895 */
896static
897sh_com_t * command_init(void)
898{
899 uid_t ff_euid;
900 sh_com_t * ext_com = NULL;
901
902 SL_ENTER(_("command_init"));
903
904 ext_com = (sh_com_t *) SH_ALLOC(sizeof(sh_com_t));
905
906 if (!ext_com)
907 {
908 SL_RETURN( NULL, _("command_init"));
909 }
910
911 sh_ext_tas_init (&(ext_com->tas));
912
913 (void) sl_get_euid(&ff_euid);
914#if 0
915 ext_com->tas.trusted_users[0] = (uid_t) 0;
916 ext_com->tas.trusted_users[1] = (uid_t) (ff_euid);
917#endif
918
919 /* ------------------------------------------------- */
920
921 set3(ext_com->type, 'l', 'o', 'g');
922 ext_com->filter = NULL;
923 ext_com->deadtime = 0;
924 ext_com->last_run = 0;
925
926 ext_com->next = NULL;
927
928 SL_RETURN( ext_com, _("command_init"));
929}
930
931/* the list of external commands
932 */
933static sh_com_t * ext_coms = NULL;
934
935/* if -1, allocation of last command has failed,
936 * thus don't fill in options
937 */
938static int ext_failed = -1;
939
940static
941int sh_ext_add_envv(const char * key, const char * val)
942{
943 int retval;
944
945 SL_ENTER(_("sh_ext_add_envv"));
946
947 if (ext_coms == NULL || ext_failed == (-1) ||
948 (key == NULL && val == NULL) ||
949 ext_coms->tas.envc >= 30)
950 {
951 SL_RETURN (-1, _("sh_ext_add_envv"));
952 }
953
954 retval = sh_ext_tas_add_envv(&(ext_coms->tas), key, val);
955
956 if (retval >= 0)
957 retval = 0;
958
959 SL_RETURN (retval, _("sh_ext_add_envv"));
960}
961
962
963
964static
965int sh_ext_init(const char * command)
966{
967 sh_com_t * retval;
968 size_t size;
969
970 SL_ENTER(_("sh_ext_init"));
971
972 if (command == NULL)
973 {
974 SL_RETURN (-1, _("sh_ext_init"));
975 }
976 size = strlen(command);
977 if (command[0] != '/' || size < 2)
978 {
979 SL_RETURN (-1, _("sh_ext_init"));
980 }
981
982 if (NULL == (retval = command_init()))
983 {
984 SL_RETURN (-1, _("sh_ext_init"));
985 }
986
987 sh_ext_tas_command(&(retval->tas), command);
988
989 if (sh.timezone != NULL)
990 {
991 (void) sh_ext_add_envv( "TZ", sh.timezone);
992 }
993
994 retval->next = ext_coms;
995 ext_coms = retval;
996 SL_RETURN (0, _("sh_ext_init"));
997}
998
999static
1000int sh_ext_uid (const char * user, /*@out@*/uid_t * uid, /*@out@*/gid_t * gid)
1001{
1002 struct passwd * tempres;
1003#if defined(HAVE_PTHREAD) && defined (_POSIX_THREAD_SAFE_FUNCTIONS) && defined(HAVE_GETPWNAM_R)
1004 struct passwd pwd;
1005 char * buffer = SH_ALLOC(SH_PWBUF_SIZE);
1006#endif
1007
1008 SL_ENTER(_("sh_ext_uid"));
1009
1010 *uid = (uid_t)-1; *gid = (gid_t)-1;
1011
1012 if (user == NULL)
1013 {
1014#if defined(HAVE_PTHREAD) && defined (_POSIX_THREAD_SAFE_FUNCTIONS) && defined(HAVE_GETPWNAM_R)
1015 SH_FREE(buffer);
1016#endif
1017 SL_RETURN (-1, _("sh_ext_uid"));
1018 }
1019
1020#if defined(HAVE_PTHREAD) && defined (_POSIX_THREAD_SAFE_FUNCTIONS) && defined(HAVE_GETPWNAM_R)
1021 sh_getpwnam_r(user, &pwd, buffer, SH_PWBUF_SIZE, &tempres);
1022#else
1023 tempres = sh_getpwnam(user);
1024#endif
1025
1026 if (NULL != tempres)
1027 {
1028 *uid = tempres->pw_uid;
1029 *gid = tempres->pw_gid;
1030#if defined(HAVE_PTHREAD) && defined (_POSIX_THREAD_SAFE_FUNCTIONS) && defined(HAVE_GETPWNAM_R)
1031 SH_FREE(buffer);
1032#endif
1033 SL_RETURN (0, _("sh_ext_uid"));
1034 }
1035
1036#if defined(HAVE_PTHREAD) && defined (_POSIX_THREAD_SAFE_FUNCTIONS) && defined(HAVE_GETPWNAM_R)
1037 SH_FREE(buffer);
1038#endif
1039 SL_RETURN (-1, _("sh_ext_uid"));
1040}
1041
1042
1043static
1044int sh_ext_add (const char * argstring, int * ntok, char * stok[])
1045{
1046 int i = 0;
1047 size_t s;
1048 char * p;
1049 char * new;
1050 size_t len;
1051
1052 SL_ENTER(_("sh_ext_add"));
1053
1054 if (NULL == argstring)
1055 {
1056 SL_RETURN((-1), _("sh_ext_add"));
1057 }
1058
1059 len = strlen(argstring) + 1;
1060 new = SH_ALLOC(len);
1061 sl_strlcpy(new, argstring, len);
1062
1063 do
1064 {
1065#if defined(HAVE_PTHREAD) && defined (_POSIX_THREAD_SAFE_FUNCTIONS) && defined(HAVE_STRTOK_R)
1066 char * saveptr;
1067 if (i == 0)
1068 p = strtok_r (new, ", \t", &saveptr);
1069 else
1070 p = strtok_r (NULL, ", \t", &saveptr);
1071#else
1072 if (i == 0)
1073 p = strtok (new, ", \t");
1074 else
1075 p = strtok (NULL, ", \t");
1076#endif
1077
1078 if (p == NULL)
1079 break;
1080
1081 s = strlen(p) + 1;
1082 if (stok[i] != NULL)
1083 SH_FREE(stok[i]);
1084 stok[i] = SH_ALLOC(s);
1085 (void) sl_strlcpy(stok[i], p, s);
1086
1087 ++i;
1088 if (i == 30)
1089 break;
1090 }
1091 while (p != NULL);
1092
1093 *ntok = i;
1094 SH_FREE(new);
1095
1096 SL_RETURN (0, _("sh_ext_add"));
1097}
1098
1099/*********************************************************
1100 *
1101 * Public functions
1102 *
1103 *
1104 *********************************************************/
1105
1106/*
1107 * -- start a new external command, and add it to the list
1108 */
1109int sh_ext_setcommand(const char * cmd)
1110{
1111 int i;
1112
1113 SL_ENTER(_("sh_ext_setcommand"));
1114 if ( (i = sh_ext_init(cmd)) < 0)
1115 ext_failed = -1;
1116 else
1117 ext_failed = 0;
1118 SL_RETURN( i, _("sh_ext_setcommand"));
1119}
1120
1121
1122/*
1123 * -- clean up the command list
1124 */
1125int sh_ext_cleanup(void)
1126{
1127 sh_com_t * retval;
1128
1129 SL_ENTER(_("sh_ext_cleanup"));
1130
1131 while (ext_coms != NULL)
1132 {
1133 retval = ext_coms;
1134 ext_coms = retval->next;
1135
1136 sh_ext_tas_free (&(retval->tas));
1137
1138 if (retval->filter)
1139 sh_filter_free (retval->filter);
1140
1141 SH_FREE(retval);
1142
1143 }
1144
1145 SL_RETURN (0, _("sh_ext_cleanup"));
1146}
1147
1148/*
1149 * -- explicitely close a command
1150 */
1151int sh_ext_close_command (const char * str)
1152{
1153 (void) str;
1154 if (ext_coms == NULL || ext_failed == (-1))
1155 return (-1);
1156 ext_failed = (-1);
1157 return 0;
1158}
1159
1160/*
1161 * -- add keywords to the OR filter
1162 */
1163int sh_ext_add_or (const char * str)
1164{
1165 if (ext_coms == NULL || ext_failed == (-1))
1166 return (-1);
1167 if (ext_coms->filter == NULL)
1168 ext_coms->filter = sh_filter_alloc();
1169 return (sh_filter_add(str, ext_coms->filter, SH_FILT_OR));
1170}
1171
1172/*
1173 * -- add keywords to the AND filter
1174 */
1175int sh_ext_add_and (const char * str)
1176{
1177 if (ext_coms == NULL || ext_failed == (-1))
1178 return (-1);
1179 if (ext_coms->filter == NULL)
1180 ext_coms->filter = sh_filter_alloc();
1181 return (sh_filter_add(str, ext_coms->filter, SH_FILT_AND));
1182}
1183
1184/*
1185 * -- add keywords to the NOT filter
1186 */
1187int sh_ext_add_not (const char * str)
1188{
1189 if (ext_coms == NULL || ext_failed == (-1))
1190 return (-1);
1191 if (ext_coms->filter == NULL)
1192 ext_coms->filter = sh_filter_alloc();
1193 return (sh_filter_add(str, ext_coms->filter, SH_FILT_NOT));
1194}
1195
1196/*
1197 * -- add keywords to the CL argument list
1198 */
1199int sh_ext_add_argv (const char * str)
1200{
1201 if (ext_coms == NULL || ext_failed == (-1))
1202 return (-1);
1203 return (sh_ext_add (str, &(ext_coms->tas.argc), ext_coms->tas.argv));
1204}
1205
1206/*
1207 * -- add a path to the environment
1208 */
1209int sh_ext_add_default (const char * dummy)
1210{
1211 char * p = NULL;
1212 int i;
1213 char dir[SH_PATHBUF];
1214
1215 SL_ENTER(_("sh_ext_add_default"));
1216 if (dummy[0] == 'n' || dummy[0] == 'N' ||
1217 dummy[0] == 'f' || dummy[0] == 'F' || dummy[0] == '0')
1218 {
1219 SL_RETURN(0, _("sh_ext_add_default"));
1220 }
1221 p = sh_unix_getUIDdir (SH_ERR_ERR, (uid_t) ext_coms->tas.run_user_uid,
1222 dir, sizeof(dir));
1223 if (p)
1224 (void) sh_ext_add_envv (_("HOME"), p);
1225 (void) sh_ext_add_envv (_("SHELL"), _("/bin/sh"));
1226 (void) sh_ext_add_envv (_("PATH"), _("/sbin:/bin:/usr/sbin:/usr/bin"));
1227 (void) sh_ext_add_envv (_("IFS"), " \n\t");
1228 i = (p == NULL ? (-1) : 0);
1229 SL_RETURN(i, _("sh_ext_add_default"));
1230}
1231
1232/*
1233 * -- add an environment variable
1234 */
1235int sh_ext_add_environ (const char * str)
1236{
1237 int i;
1238
1239 SL_ENTER(_("sh_ext_add_environ"));
1240 i = sh_ext_add_envv (NULL, str);
1241 SL_RETURN(i, _("sh_ext_add_environ"));
1242}
1243
1244/*
1245 * -- set deadtime
1246 */
1247int sh_ext_deadtime (const char * str)
1248{
1249 long deadtime = 0;
1250 char * tail = NULL;
1251
1252 SL_ENTER(_("sh_ext_deadtime"));
1253
1254 if (ext_coms == NULL || ext_failed == (-1) || str == NULL)
1255 {
1256 SL_RETURN (-1, _("sh_ext_deadtime"));
1257 }
1258 deadtime = strtol(str, &tail, 10);
1259 if (tail == str || deadtime < 0 || deadtime == LONG_MAX)
1260 {
1261 SL_RETURN (-1, _("sh_ext_deadtime"));
1262 }
1263
1264 ext_coms->deadtime = (time_t) deadtime;
1265 SL_RETURN (0, _("sh_ext_deadtime"));
1266}
1267
1268/*
1269 * -- define type
1270 */
1271int sh_ext_type (const char * str)
1272{
1273 SL_ENTER(_("sh_ext_type"));
1274
1275 if (ext_coms == NULL || ext_failed == (-1) || str == NULL)
1276 {
1277 SL_RETURN((-1), _("sh_ext_type"));
1278 }
1279
1280 if (strlen(str) != 3)
1281 {
1282 SL_RETURN((-1), _("sh_ext_type"));
1283 }
1284
1285 set3(ext_coms->type, str[0], str[1], str[2]);
1286
1287 if (str[0] == 'l' && str[1] == 'o' && str[2] == 'g')
1288 ext_coms->tas.rw = 'w';
1289 else if (str[0] == 's' && str[1] == 'r' && str[2] == 'v')
1290 ext_coms->tas.rw = 'w';
1291 else if (str[0] == 'm' && str[1] == 'o' && str[2] == 'n')
1292 ext_coms->tas.rw = 'r';
1293 else
1294 {
1295 SL_RETURN((-1), _("sh_ext_type"));
1296 }
1297
1298 SL_RETURN(0, _("sh_ext_type"));
1299}
1300
1301
1302
1303/*
1304 * -- define checksum
1305 */
1306int sh_ext_checksum (const char * str)
1307{
1308 SL_ENTER(_("sh_ext_checksum"));
1309 if (ext_coms == NULL || ext_failed == (-1) || str == NULL)
1310 {
1311 SL_RETURN((-1), _("sh_ext_checksum"));
1312 }
1313
1314 if (sl_strlen(str) != KEY_LEN)
1315 {
1316 SL_RETURN((-1), _("sh_ext_checksum"));
1317 }
1318
1319 (void) sl_strlcpy (ext_coms->tas.checksum, str, KEY_LEN+1);
1320
1321 SL_RETURN((0), _("sh_ext_checksum"));
1322}
1323
1324/*
1325 * -- choose privileges
1326 */
1327int sh_ext_priv (const char * c)
1328{
1329
1330 uid_t me_uid;
1331 gid_t me_gid;
1332
1333 SL_ENTER(_("sh_ext_priv"));
1334 if (0 == sh_ext_uid (c, &me_uid, &me_gid))
1335 {
1336 ext_coms->tas.run_user_uid = me_uid;
1337 ext_coms->tas.run_user_gid = me_gid;
1338 if (me_uid != (uid_t) 0)
1339 ext_coms->tas.privileged = 0;
1340 SL_RETURN((0), _("sh_ext_priv"));
1341 }
1342
1343 SL_RETURN (-1, _("sh_ext_priv"));
1344}
1345
1346
1347
1348
1349/*
1350 * -- check filters
1351 */
1352static int sh_ext_filter (char * message, sh_com_t * task)
1353{
1354 time_t now_time;
1355
1356 SL_ENTER(_("sh_ext_filter"));
1357
1358 if (task->filter)
1359 {
1360 if (0 != sh_filter_filter (message, task->filter))
1361 {
1362 SL_RETURN ((-1), _("sh_ext_filter"));
1363 }
1364 }
1365
1366 /* Filter passed, check deadtime */
1367
1368 if (task->deadtime != (time_t) 0)
1369 {
1370 now_time = time (NULL);
1371
1372 if (task->last_run == (time_t) 0)
1373 {
1374 task->last_run = now_time;
1375 }
1376 else if ((time_t)(now_time-task->last_run) < task->deadtime)
1377 {
1378 SL_RETURN ((-1), _("sh_ext_filter"));
1379 }
1380 else
1381 {
1382 task->last_run = now_time;
1383 }
1384 }
1385
1386 SL_RETURN ((0), _("sh_ext_filter"));
1387}
1388
1389
1390
1391/*
1392 * -- execute external script/program
1393 */
1394int sh_ext_execute (char t1, char t2, char t3, /*@null@*/char * message,
1395 size_t msg_siz)
1396{
1397 int caperr;
1398 sh_com_t * listval = ext_coms;
1399 int status = 0;
1400 char * tmp;
1401 char errbuf[SH_ERRBUF_SIZE];
1402
1403 static int some_error = 0;
1404
1405 struct sigaction new_act;
1406 struct sigaction old_act;
1407
1408 SL_ENTER(_("sh_ext_execute"));
1409
1410 PDBG_OPEN;
1411
1412 if (listval == NULL || message == NULL)
1413 {
1414 SL_RETURN ((-1), _("sh_ext_execute"));
1415 }
1416
1417 PDBG(-1);
1418
1419 if (msg_siz == 0)
1420 msg_siz = sl_strlen(message);
1421
1422
1423 /* ignore SIGPIPE (instead get EPIPE if connection is closed)
1424 */
1425 new_act.sa_handler = SIG_IGN;
1426 (void) retry_sigaction (FIL__, __LINE__, SIGPIPE, &new_act, &old_act);
1427
1428 while (listval != NULL)
1429 {
1430 PDBG_OPEN;
1431 PDBG(-2);
1432 if (t1 == listval->type[0] &&
1433 t2 == listval->type[1] &&
1434 t3 == listval->type[2] &&
1435 0 == sh_ext_filter (message, listval))
1436 {
1437 PDBG(-3);
1438
1439 if (0 != (caperr = sl_get_cap_sub()))
1440 {
1441 sh_error_handle((-1), FIL__, __LINE__, caperr, MSG_E_SUBGEN,
1442 sh_error_message (caperr, errbuf, sizeof(errbuf)),
1443 _("sl_get_cap_sub"));
1444 }
1445 if (0 == sh_ext_popen (&(listval->tas)))
1446 {
1447 PDBG_OPEN;
1448 PDBG(-4);
1449 if (NULL != listval->tas.pipe && listval->tas.rw == 'w')
1450 {
1451 PDBG(-5);
1452 if (message != NULL)
1453 {
1454 PDBG(-6);
1455 status = (int) write (listval->tas.pipeFD,
1456 message, msg_siz);
1457 if (status >= 0)
1458 status = (int) write (listval->tas.pipeFD, "\n", 1);
1459 }
1460 PDBG_D(status);
1461 if (status >= 0)
1462 status = (int) write (listval->tas.pipeFD, "[", 1);
1463 PDBG_D(status);
1464 if (status >= 0)
1465 status = (int) write (listval->tas.pipeFD, "E", 1);
1466 PDBG_D(status);
1467 if (status >= 0)
1468 status = (int) write (listval->tas.pipeFD, "O", 1);
1469 PDBG_D(status);
1470 if (status >= 0)
1471 status = (int) write (listval->tas.pipeFD, "F", 1);
1472 PDBG_D(status);
1473 if (status >= 0)
1474 status = (int) write (listval->tas.pipeFD, "]", 1);
1475 PDBG_D(status);
1476 if (status >= 0)
1477 status = (int) write (listval->tas.pipeFD, "\n", 1);
1478 PDBG_D(status);
1479 if (status >= 0)
1480 {
1481 some_error = 0;
1482 }
1483 if ((status < 0) && (some_error == 0))
1484 {
1485 some_error = 1;
1486 PDBG_S("some error");
1487 PDBG_D(status);
1488 tmp = sh_util_safe_name (listval->tas.command);
1489
1490 if (tmp)
1491 {
1492 if (listval->tas.privileged == 0 &&
1493 (0 == getuid() || 0 != sl_is_suid()) )
1494 sh_error_handle((-1), FIL__, __LINE__, 0,
1495 MSG_NOEXEC,
1496 (UID_CAST) listval->tas.run_user_uid,
1497 tmp);
1498 else
1499 sh_error_handle((-1), FIL__, __LINE__, 0,
1500 MSG_NOEXEC,
1501 (UID_CAST) getuid(), tmp);
1502
1503 SH_FREE(tmp);
1504 }
1505
1506 }
1507 PDBG(-7);
1508 (void) fflush(listval->tas.pipe);
1509 }
1510 PDBG(-8);
1511 (void) sh_ext_pclose(&(listval->tas));
1512 }
1513 else
1514 {
1515 PDBG_OPEN;
1516 PDBG_S("0 != sh_ext_popen()");
1517 }
1518 if (0 != (caperr = sl_drop_cap_sub()))
1519 {
1520 sh_error_handle((-1), FIL__, __LINE__, caperr, MSG_E_SUBGEN,
1521 sh_error_message (caperr, errbuf, sizeof(errbuf)),
1522 _("sl_drop_cap_sub"));
1523 }
1524
1525 }
1526 listval = listval->next;
1527 }
1528 PDBG_OPEN;
1529 PDBG_S("no more commands");
1530
1531 /* restore old signal handler
1532 */
1533 (void) retry_sigaction (FIL__, __LINE__, SIGPIPE, &old_act, NULL);
1534 PDBG_S("return");
1535 PDBG_CLOSE;
1536
1537 SL_RETURN ((0), _("sh_ext_execute"));
1538}
1539
1540
1541/* #if defined(WITH_EXTERNAL) */
1542#endif
Note: See TracBrowser for help on using the repository browser.