source: trunk/src/sh_processcheck.c@ 225

Last change on this file since 225 was 210, checked in by katerina, 16 years ago

Fix for ticket #136 (compile error); also enhance testsuite to catch compiler warnings.

File size: 34.9 KB
Line 
1/* SAMHAIN file system integrity testing */
2/* Copyright (C) 2006 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 *
22 * This file provides a module for samhain to check for hidden/faked/missing
23 * processes on the host.
24 *
25 */
26
27#include "config_xor.h"
28
29#define _XOPEN_SOURCE 500
30
31#include <stdio.h>
32#include <stdlib.h>
33#include <ctype.h>
34#include <string.h>
35#include <sys/types.h>
36#include <dirent.h>
37#include <sys/stat.h>
38#include <signal.h>
39#include <unistd.h>
40
41#ifdef _POSIX_PRIORITY_SCHEDULING
42#include <sched.h>
43#endif
44
45#ifdef HAVE_GETPRIORITY
46#include <errno.h>
47#include <sys/resource.h>
48#endif
49
50#ifdef HAVE_SYS_STATVFS_H
51#include <sys/statvfs.h>
52#endif
53
54
55#ifdef HAVE_REGEX_H
56#include <regex.h>
57#endif
58
59#include "samhain.h"
60#include "sh_modules.h"
61#include "sh_processcheck.h"
62#include "sh_utils.h"
63#include "sh_error.h"
64#include "sh_extern.h"
65#include "sh_calls.h"
66#include "sh_pthread.h"
67
68#ifdef SH_USE_PROCESSCHECK
69
70#define FIL__ _("sh_processcheck.c")
71
72#ifdef __linux__
73#define PS_THREADS
74#endif
75
76/* We won't want to build this into yule
77 */
78#if defined (SH_WITH_CLIENT) || defined (SH_STANDALONE)
79
80SH_MUTEX_STATIC(mutex_proc_check, PTHREAD_MUTEX_INITIALIZER);
81
82/* sh_prochk_maxpid is one more than the largest pid
83 */
84static size_t sh_prochk_minpid = 0x0001;
85static size_t sh_prochk_maxpid = 0x8000;
86static size_t sh_prochk_size = 0;
87
88static int ShProchkActive = S_TRUE;
89static short * sh_prochk_res = NULL;
90
91static char * sh_prochk_pspath = NULL;
92static char * sh_prochk_psarg = NULL;
93
94#define SH_PROCHK_INTERVAL 300
95static time_t sh_prochk_interval = SH_PROCHK_INTERVAL;
96static int sh_prochk_severity = SH_ERR_SEVERE;
97static int sh_prochk_openvz = S_FALSE;
98
99static int sh_prochk_set_maxpid (const char * str);
100static int sh_prochk_set_minpid (const char * str);
101static int sh_prochk_set_active (const char *str);
102static int sh_prochk_add_process (const char *str);
103static int sh_prochk_set_pspath (const char *str);
104static int sh_prochk_set_psarg (const char *str);
105static int sh_prochk_set_interval(const char *str);
106static int sh_prochk_set_severity(const char *str);
107static int sh_prochk_set_openvz (const char *str);
108
109sh_rconf sh_prochk_table[] = {
110 {
111 N_("severityprocesscheck"),
112 sh_prochk_set_severity,
113 },
114 {
115 N_("processcheckexists"),
116 sh_prochk_add_process,
117 },
118 {
119 N_("processcheckactive"),
120 sh_prochk_set_active,
121 },
122 {
123 N_("processcheckminpid"),
124 sh_prochk_set_minpid,
125 },
126 {
127 N_("processcheckmaxpid"),
128 sh_prochk_set_maxpid,
129 },
130 {
131 N_("processcheckpspath"),
132 sh_prochk_set_pspath,
133 },
134 {
135 N_("processcheckpsarg"),
136 sh_prochk_set_psarg,
137 },
138 {
139 N_("processcheckinterval"),
140 sh_prochk_set_interval,
141 },
142 {
143 N_("processcheckisopenvz"),
144 sh_prochk_set_openvz,
145 },
146 {
147 NULL,
148 NULL
149 }
150};
151
152#define SH_PROC_MISSING 1
153#define SH_PROC_FAKED 2
154#define SH_PROC_HIDDEN 4
155#define SH_PROC_EXISTS 8
156
157#ifndef HAVE_LSTAT
158#define lstat(x,y) stat(x,y)
159#endif /* HAVE_LSTAT */
160
161#if defined(S_IFLNK) && !defined(S_ISLNK)
162#define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK)
163#else
164#if !defined(S_ISLNK)
165#define S_ISLNK(mode) (0)
166#endif
167#endif
168
169static const short SH_PR_PS = 0x0001;
170
171static const short SH_PR_GETSID = 0x0002;
172static const short SH_PR_KILL = 0x0004;
173static const short SH_PR_GETPGID = 0x0008;
174
175static const short SH_PR_LSTAT = 0x0010;
176static const short SH_PR_OPENDIR = 0x0020;
177static const short SH_PR_CHDIR = 0x0040;
178static const short SH_PR_SCHED = 0x0080;
179
180static const short SH_PR_PRIORITY = 0x0100;
181static const short SH_PR_STATVSF = 0x0200;
182
183static const short SH_PR_PS2 = 0x1000;
184static const short SH_PR_PS_ANY = 0x2000;
185static const short SH_PR_ALL = 0x4000;
186static const short SH_PR_ANY = 0x8000;
187
188/* /proc:
189 * linux: /proc/pid/exe
190 * freebsd: /proc/pid/file
191 * solaris10: /proc/pid/path/a.out
192 */
193static char * get_user_and_path (pid_t pid, char * user, size_t usrlen)
194{
195 extern char * sh_unix_getUIDname (int level, uid_t uid, char * out, size_t len);
196
197 char path[128];
198 char * buf;
199 struct stat sbuf;
200 int len;
201 char * tmp;
202
203 sl_snprintf (path, sizeof(path), _("/proc/%ld/exe"), (unsigned long) pid);
204
205 if (0 == retry_lstat(FIL__, __LINE__, path, &sbuf) && S_ISLNK(sbuf.st_mode))
206 {
207 goto linkread;
208 }
209
210 sl_snprintf (path, sizeof(path), _("/proc/%ld/file"), (unsigned long) pid);
211
212 if (0 == retry_lstat(FIL__, __LINE__, path, &sbuf) && S_ISLNK(sbuf.st_mode))
213 {
214 goto linkread;
215 }
216
217 sl_snprintf (path, sizeof(path), _("/proc/%ld/path/a.out"), (unsigned long) pid);
218
219 if (0 == retry_lstat(FIL__, __LINE__, path, &sbuf) && S_ISLNK(sbuf.st_mode))
220 {
221 goto linkread;
222 }
223
224 return NULL;
225
226 linkread:
227
228 buf = SH_ALLOC(PATH_MAX);
229 len = readlink(path, buf, PATH_MAX); /* flawfinder: ignore */
230 len = (len >= PATH_MAX) ? (PATH_MAX-1) : len;
231
232 if (len > 0)
233 {
234 buf[len] = '\0';
235 }
236 else
237 {
238 SH_FREE(buf);
239 return NULL;
240 }
241
242 tmp = sh_unix_getUIDname (SH_ERR_ALL, sbuf.st_uid, user, usrlen);
243
244 if (!tmp)
245 sl_snprintf (user, usrlen, "%ld", (unsigned long) sbuf.st_uid);
246
247 return buf;
248}
249
250
251struct watchlist {
252 char * str;
253 unsigned long pid;
254#ifdef HAVE_REGEX_H
255 regex_t preg;
256#endif
257 int seen;
258
259 struct watchlist *next;
260};
261
262static struct watchlist * process_check = NULL;
263
264static struct watchlist * list_missing = NULL;
265static struct watchlist * list_fake = NULL;
266static struct watchlist * list_hidden = NULL;
267
268/* recursively remove all list entries
269 */
270static void kill_list (struct watchlist * head)
271{
272 if (head->next)
273 kill_list (head->next);
274
275 if (head->str)
276 SH_FREE(head->str);
277 SH_FREE(head);
278
279 return;
280}
281
282
283/* check the list for old entries; clean out old entries; reset others
284 * Return number of non-obsolete entries
285 */
286static size_t clean_list (struct watchlist ** head_ptr)
287{
288 size_t count = 0;
289 struct watchlist * ptr = *head_ptr;
290 struct watchlist * pre = *head_ptr;
291
292 while (ptr)
293 {
294 if (ptr->seen == S_FALSE) /* obsolete entry */
295 {
296 if (ptr == pre) /* at head */
297 {
298 ptr = pre->next;
299 *head_ptr = pre->next;
300 if (pre->str)
301 SH_FREE(pre->str);
302 SH_FREE(pre);
303 pre = ptr;
304 }
305 else
306 {
307 pre->next = ptr->next;
308 if (ptr->str)
309 SH_FREE(ptr->str);
310 SH_FREE(ptr);
311 ptr = pre->next;
312 }
313 }
314 else
315 {
316 ++count;
317 ptr->seen = S_FALSE; /* reset status */
318 pre = ptr;
319 ptr = ptr->next;
320 }
321 }
322 return count;
323}
324
325/* check if process is in list; if not, add it and return false
326 */
327static int is_in_list (struct watchlist ** head_ptr,
328 char * str, unsigned long pid)
329{
330 struct watchlist * ptr = *head_ptr;
331
332 if (str)
333 {
334 while (ptr)
335 {
336 if (ptr->str && (0 == strcmp(str, ptr->str)))
337 {
338 ptr->seen = S_TRUE;
339 return S_TRUE;
340 }
341 ptr = ptr->next;
342 }
343 }
344 else
345 {
346 while (ptr)
347 {
348 if (ptr->pid == pid)
349 {
350 ptr->seen = S_TRUE;
351 return S_TRUE;
352 }
353 ptr = ptr->next;
354 }
355 }
356
357 ptr = SH_ALLOC(sizeof(struct watchlist));
358
359 if (str)
360 {
361 ptr->str = sh_util_strdup(str);
362 }
363 else
364 {
365 ptr->str = NULL;
366 ptr->pid = pid;
367 }
368 ptr->next = *head_ptr;
369 ptr->seen = S_TRUE;
370 *head_ptr = ptr;
371
372 return S_FALSE;
373}
374
375static int is_in_watchlist (const char *str, unsigned long num)
376{
377 struct watchlist * list = process_check;
378
379 while (list)
380 {
381#ifdef HAVE_REGEX_H
382 if (0 == regexec(&(list->preg), str, 0, NULL, 0))
383 {
384 list->seen = S_TRUE;
385 list->pid = num;
386 return S_TRUE;
387 }
388#else
389 if (strstr(str, list->str))
390 {
391 list->seen = S_TRUE;
392 list->pid = num;
393 return S_TRUE;
394 }
395#endif
396 list = list->next;
397 }
398 return S_FALSE;
399}
400
401/* These variables are not used anywhere. They only exist
402 * to assign &userold, &user to them, which keeps gcc from
403 * putting them into a register, and avoids the 'clobbered
404 * by longjmp' warning. And no, 'volatile' proved insufficient.
405 */
406static void * sh_dummy_watchlist = NULL;
407
408static void check_watchlist (short * res)
409{
410 struct watchlist * list = process_check;
411 char * tmp;
412 size_t indx;
413
414 /* Take the address to keep gcc from putting them into registers.
415 * Avoids the 'clobbered by longjmp' warning.
416 */
417 sh_dummy_watchlist = (void*) &list;
418
419 while (list)
420 {
421 if (list->seen == S_FALSE)
422 {
423 /* avoid repetition of messages
424 */
425 if (S_FALSE == is_in_list(&list_missing, list->str, 0))
426 {
427 SH_MUTEX_LOCK(mutex_thread_nolog);
428 tmp = sh_util_safe_name (list->str);
429 sh_error_handle(sh_prochk_severity, FIL__, __LINE__, 0,
430 MSG_PCK_MISS,
431 tmp);
432 SH_MUTEX_UNLOCK(mutex_thread_nolog);
433 }
434 }
435 else
436 {
437 indx = list->pid - sh_prochk_minpid;
438
439 if (list->pid < sh_prochk_maxpid && list->pid >= sh_prochk_minpid &&
440 ((res[indx] & SH_PR_ANY) == 0) && /* not found */
441 ((res[indx] & SH_PR_PS) != 0) && /* seen in first ps */
442 ((res[indx] & SH_PR_PS2) != 0)) /* seen in second ps */
443 {
444 /* fake process, thus considered missing
445 */
446 if (S_FALSE == is_in_list(&list_missing, list->str, 0))
447 {
448 SH_MUTEX_LOCK(mutex_thread_nolog);
449 tmp = sh_util_safe_name (list->str);
450 sh_error_handle(sh_prochk_severity, FIL__, __LINE__, 0,
451 MSG_PCK_MISS,
452 tmp);
453 SH_MUTEX_UNLOCK(mutex_thread_nolog);
454 SH_FREE(tmp);
455 }
456 }
457 list->seen = S_FALSE;
458 }
459 list = list->next;
460 }
461}
462
463/* Add 'str' to the list of watched processes for which
464 * existence should be checked.
465 */
466int sh_prochk_add_process (const char *str)
467{
468 struct watchlist *new;
469 int status;
470 char errbuf[256];
471
472 SL_ENTER(_("sh_prochk_add_process"));
473
474 if( str == NULL )
475 SL_RETURN(-1, _("sh_prochk_add_process") );
476
477 new = SH_ALLOC(sizeof(struct watchlist));
478 new->next = process_check;
479 new->str = sh_util_strdup(str);
480#ifdef HAVE_REGEX_H
481 status = regcomp(&(new->preg), str, REG_NOSUB|REG_EXTENDED);
482 if (status != 0)
483 {
484 regerror(status, &(new->preg), errbuf, sizeof(errbuf));
485 SH_MUTEX_LOCK(mutex_thread_nolog);
486 sh_error_handle((-1), FIL__, __LINE__, status, MSG_E_SUBGEN,
487 errbuf, _("sh_processes_add_process"));
488 SH_MUTEX_UNLOCK(mutex_thread_nolog);
489 SH_FREE(new->str);
490 SH_FREE(new);
491 SL_RETURN(-1, _("sh_prochk_add_process") );
492 }
493#endif
494 new->pid = 0;
495 new->seen = S_FALSE;
496
497 process_check = new;
498 SL_RETURN(0, _("sh_prochk_add_process") );
499}
500
501/* severity
502 */
503int sh_prochk_set_severity (const char * c)
504{
505 char tmp[32];
506 tmp[0] = '='; tmp[1] = '\0';
507 sl_strlcat (tmp, c, 32);
508 return sh_error_set_level (tmp, &sh_prochk_severity);
509}
510
511
512
513/* Path to ps
514 */
515int sh_prochk_set_pspath(const char *str)
516{
517 SL_ENTER(_("sh_prochk_set_pspath"));
518
519 if (!str && ('/' != str[0]))
520 SL_RETURN((-1), _("sh_prochk_set_pspath"));
521 if (sh_prochk_pspath)
522 SH_FREE(sh_prochk_pspath);
523 sh_prochk_pspath = sh_util_strdup (str);
524
525 SL_RETURN((0), _("sh_prochk_set_pspath"));
526
527}
528
529/* argument for ps
530 */
531int sh_prochk_set_psarg(const char *str)
532{
533 SL_ENTER(_("sh_prochk_set_psarg"));
534
535 if (sh_prochk_psarg)
536 SH_FREE(sh_prochk_psarg);
537 sh_prochk_psarg = sh_util_strdup (str);
538
539 SL_RETURN((0), _("sh_prochk_set_psarg"));
540
541}
542
543
544/* Decide if we're active.
545 */
546int sh_prochk_set_active(const char *str)
547{
548 int value;
549
550 SL_ENTER(_("sh_prochk_set_active"));
551
552 value = sh_util_flagval(str, &ShProchkActive);
553
554 SL_RETURN((value), _("sh_prochk_set_active"));
555}
556
557/* Are we on openvz.
558 */
559static int openvz_hidden = 0;
560
561int sh_prochk_set_openvz(const char *str)
562{
563 int value;
564
565 SL_ENTER(_("sh_prochk_set_openvz"));
566
567 value = sh_util_flagval(str, &sh_prochk_openvz);
568
569 if (sh_prochk_openvz != S_FALSE) {
570 openvz_hidden = 1;
571 }
572
573 SL_RETURN((value), _("sh_prochk_set_openvz"));
574}
575
576/* Minimum PID
577 */
578int sh_prochk_set_minpid(const char * str)
579{
580 size_t value;
581 char * foo;
582 int retval = 0;
583
584 SL_ENTER(_("sh_prochk_set_minpid"));
585
586 value = (size_t) strtoul(str, &foo, 0);
587 if (*foo != '\0')
588 retval = -1;
589 else
590 sh_prochk_minpid = value;
591
592 SL_RETURN((retval), _("sh_prochk_set_minpid"));
593}
594
595/* Maximum PID
596 */
597static int userdef_maxpid = 0;
598
599int sh_prochk_set_maxpid(const char * str)
600{
601 size_t value;
602 char * foo;
603 int retval = -1;
604
605 SL_ENTER(_("sh_prochk_set_maxpid"));
606
607 value = (size_t) strtoul(str, &foo, 0);
608
609 if (*foo == '\0' && SL_TRUE == sl_ok_adds(value, 1)) {
610 sh_prochk_maxpid = value + 1;
611 userdef_maxpid = 1;
612 }
613
614 SL_RETURN((retval), _("sh_prochk_set_maxpid"));
615}
616
617int sh_prochk_set_interval (const char * c)
618{
619 int retval = 0;
620 long val;
621
622 SL_ENTER(_("sh_prochk_set_interval"));
623 val = strtol (c, (char **)NULL, 10);
624 if (val <= 0)
625 {
626 SH_MUTEX_LOCK(mutex_thread_nolog);
627 sh_error_handle ((-1), FIL__, __LINE__, EINVAL, MSG_EINVALS,
628 _("process check interval"), c);
629 SH_MUTEX_UNLOCK(mutex_thread_nolog);
630 retval = -1;
631 }
632
633 sh_prochk_interval = (time_t) val;
634 SL_RETURN(0, _("sh_prochk_set_interval"));
635}
636
637
638
639/* Recurse to the end of the list and then free the data as we return
640 * back up towards the start, making sure to free any strdupped strings
641 */
642static void sh_prochk_free_list(struct watchlist *head)
643{
644 if ( head != NULL )
645 {
646 sh_prochk_free_list(head->next);
647 if (head->str)
648 SH_FREE(head->str);
649#ifdef HAVE_REGEX_H
650 regfree(&(head->preg));
651#endif
652 SH_FREE(head);
653 }
654 return;
655}
656
657#if defined(__linux__)
658#define PROC_PID_MAX _("/proc/sys/kernel/pid_max")
659
660static int proc_max_pid (size_t * procpid)
661{
662 char * ret;
663 unsigned long pid;
664 FILE * fd;
665 char str[128];
666 char * ptr;
667
668 SL_ENTER(_("proc_max_pid"));
669
670 if (userdef_maxpid != 0)
671 SL_RETURN((-1), _("proc_max_pid"));
672
673 if (0 == access(PROC_PID_MAX, R_OK)) /* flawfinder: ignore */
674 {
675 if (NULL != (fd = fopen(PROC_PID_MAX, "r")))
676 {
677 str[0] = '\0';
678 ret = fgets(str, 128, fd);
679 if (ret && *str != '\0')
680 {
681 pid = strtoul(str, &ptr, 0);
682 if (*ptr == '\0' || *ptr == '\n')
683 {
684 fclose(fd);
685 *procpid = (size_t) pid;
686 SL_RETURN(0, _("proc_max_pid"));
687 }
688 }
689 fclose(fd);
690 }
691 }
692 SL_RETURN((-1), _("proc_max_pid"));
693}
694#else
695static int proc_max_pid(size_t * dummy)
696{
697 (void) dummy;
698 return -1;
699}
700#endif
701
702static void sh_processes_tlist (char * list, size_t len, short res)
703{
704 if (res & SH_PR_PS) sl_strlcat(list, _(" ps(initial)"), len);
705 if (res & SH_PR_CHDIR) sl_strlcat(list, _(" chdir"), len);
706 if (res & SH_PR_OPENDIR) sl_strlcat(list, _(" opendir"), len);
707 if (res & SH_PR_LSTAT) sl_strlcat(list, _(" lstat"), len);
708 if (res & SH_PR_PRIORITY) sl_strlcat(list, _(" getpriority"), len);
709 if (res & SH_PR_SCHED) sl_strlcat(list, _(" sched_getparam"), len);
710 if (res & SH_PR_GETSID) sl_strlcat(list, _(" getsid"), len);
711 if (res & SH_PR_GETPGID) sl_strlcat(list, _(" getpgid"), len);
712 if (res & SH_PR_KILL) sl_strlcat(list, _(" kill"), len);
713 if (res & SH_PR_STATVSF) sl_strlcat(list, _(" statvfs"), len);
714 if (res & SH_PR_PS2) sl_strlcat(list, _(" ps(final)"), len);
715 return;
716}
717
718
719static short sh_processes_check (pid_t pid, short res)
720{
721 int have_checks = 0;
722 int need_checks = 0;
723#ifdef HAVE_PROCFS
724 char path[128];
725 struct stat buf;
726 DIR * dir;
727 int retval;
728#ifdef HAVE_STATVFS
729 struct statvfs vfsbuf;
730#endif
731#endif
732
733#if !defined(sun) && !defined(__sun) && !defined(__sun__)
734#ifdef _POSIX_PRIORITY_SCHEDULING
735 struct sched_param p;
736#endif
737#endif
738
739 if (0 == kill(pid, 0))
740 {
741 res |= SH_PR_KILL; res |= SH_PR_ANY; ++have_checks;
742 ++need_checks;
743 }
744 else if (errno != EPERM)
745 {
746 ++need_checks;
747 }
748
749
750#ifdef HAVE_GETPGID
751 if ((pid_t)-1 != getpgid(pid))
752 {
753 res |= SH_PR_GETPGID; res |= SH_PR_ANY; ++have_checks;
754 }
755 ++need_checks;
756#endif
757
758#ifdef HAVE_GETSID
759 if ((pid_t)-1 != getsid(pid))
760 {
761 res |= SH_PR_GETSID; res |= SH_PR_ANY; ++have_checks;
762 }
763 ++need_checks;
764#endif
765
766 /* sched_getparam() is broken on solaris 10, may segfault in librt
767 */
768#if !defined(sun) && !defined(__sun) && !defined(__sun__)
769#ifdef _POSIX_PRIORITY_SCHEDULING
770 if (0 == sched_getparam (pid, &p))
771 {
772 res |= SH_PR_SCHED; res |= SH_PR_ANY; ++have_checks;
773 }
774 ++need_checks;
775#endif
776#endif
777
778#ifdef HAVE_GETPRIORITY
779 errno = 0;
780 if (((-1) == getpriority (PRIO_PROCESS, (int) pid)) && (errno == ESRCH));
781 else
782 {
783 res |= SH_PR_PRIORITY; res |= SH_PR_ANY; ++have_checks;
784 }
785 ++need_checks;
786#endif
787
788#ifdef HAVE_PROCFS
789 sl_snprintf (path, sizeof(path), "/proc/%ld", (unsigned long) pid);
790
791 do {
792 retval = lstat (path, &buf);
793 } while (retval < 0 && errno == EINTR);
794
795 if (0 == retval)
796 {
797 res |= SH_PR_LSTAT; res |= SH_PR_ANY; ++have_checks;
798 }
799 ++need_checks;
800
801 if (NULL != (dir = opendir(path)))
802 {
803 res |= SH_PR_OPENDIR; res |= SH_PR_ANY; ++have_checks;
804 closedir(dir);
805 }
806 ++need_checks;
807
808#ifdef HAVE_STATVFS
809 do {
810 retval = statvfs (path, &vfsbuf);
811 } while (retval < 0 && errno == EINTR);
812
813 if (0 == retval)
814 {
815 res |= SH_PR_STATVSF; res |= SH_PR_ANY; ++have_checks;
816 }
817 ++need_checks;
818#endif
819
820#if !defined(SH_PROFILE)
821 if (0 == chdir(path))
822 {
823 res |= SH_PR_CHDIR; res |= SH_PR_ANY; ++have_checks;
824 do {
825 retval = chdir ("/");
826 } while (retval < 0 && errno == EINTR);
827 }
828 ++need_checks;
829#endif
830#endif
831
832 if (have_checks == need_checks)
833 {
834 res |= SH_PR_ALL;
835 }
836 return res;
837}
838
839extern int flag_err_debug;
840
841static int sh_processes_readps (FILE * in, short * res,
842 char * str, size_t len,
843 short flag, pid_t pid)
844{
845 int cc;
846 unsigned int lnum = 0;
847 unsigned long num = 0;
848 char c;
849 unsigned int pos = 0;
850 char tstr[256];
851 enum { SKIP_TO_WS, SKIP_WS, SKIP_TO_WS2, SKIP_WS2, GET_NUM, SKIP_END, GET_NUM2 } line;
852
853 SL_ENTER(_("sh_processes_readps"));
854
855 if (!in) {
856 SL_RETURN((-1), _("sh_processes_readps"));
857 }
858
859 tstr[(sizeof(tstr)-1)] = '\0';
860 tstr[0] = '\0';
861 line = SKIP_END; /* Skip 1st line */
862
863 do
864 {
865 cc = fgetc(in);
866
867 if (EOF == cc)
868 {
869 if (feof(in))
870 {
871 break;
872 }
873 else if (errno == EAGAIN)
874 {
875 clearerr(in);
876 continue;
877 }
878#ifdef HOST_IS_OPENBSD
879 else if (errno == ENODEV)
880 {
881 clearerr(in);
882 continue;
883 }
884#endif
885 else
886 {
887 char errbuf[SH_ERRBUF_SIZE];
888
889 /* SH_MUTEX_LOCK(mutex_thread_nolog) is in caller */
890 sh_error_handle(SH_ERR_ALL, FIL__, __LINE__, errno, MSG_E_SUBGEN,
891 sh_error_message(errno, errbuf, sizeof(errbuf)),
892 _("sh_processes_readps"));
893 break;
894 }
895 }
896
897 c = (char) cc;
898
899 if (pos < (sizeof(tstr)-1))
900 {
901 tstr[pos] = c; ++pos;
902 }
903
904 switch(line)
905 {
906 case SKIP_END:
907 if (c == '\n')
908 {
909 tstr[pos-1] = '\0';
910 if (flag_err_debug == SL_TRUE)
911 {
912 /* SH_MUTEX_LOCK(mutex_thread_nolog) is in caller */
913 sh_error_handle(SH_ERR_ALL, FIL__, __LINE__, num,
914 MSG_E_SUBGEN,
915 tstr,
916 _("sh_processes_readps"));
917 }
918 /* fprintf(stderr, "<%ld> %s\n", num, tstr); */
919 line = SKIP_WS; pos = 0;
920 if (str != NULL && num == (unsigned long) pid)
921 sl_strlcpy(str, tstr, len);
922 if (lnum != 0)
923 is_in_watchlist (tstr, num);
924 ++lnum;
925 }
926 break;
927 case SKIP_TO_WS:
928 if (!isspace(cc))
929 break;
930 line = SKIP_WS;
931 /* fallthrough */
932 case SKIP_WS:
933 if (isspace(cc))
934 break;
935 num = 0;
936 line = GET_NUM;
937 /* fallthrough */
938 case GET_NUM:
939 if (isdigit(cc))
940 {
941 num = num * 10 + (c - '0');
942 break;
943 }
944 else if (isspace(cc))
945 {
946#ifdef PS_THREADS
947 num = 0;
948 line = SKIP_WS2;
949#else
950 if (num < sh_prochk_maxpid && num >= sh_prochk_minpid)
951 {
952 res[num - sh_prochk_minpid] |= flag;
953 }
954 line = SKIP_END;
955#endif
956 break;
957 }
958 else
959 {
960 line = SKIP_TO_WS;
961 break;
962 }
963 case SKIP_TO_WS2:
964 if (!isspace(cc))
965 break;
966 line = SKIP_WS2;
967 /* fallthrough */
968 case SKIP_WS2:
969 if (isspace(cc))
970 break;
971 num = 0;
972 line = GET_NUM2;
973 /* fallthrough */
974 case GET_NUM2:
975 if (isdigit(cc))
976 {
977 num = num * 10 + (c - '0');
978 break;
979 }
980 else if (isspace(cc))
981 {
982 if (num < sh_prochk_maxpid && num >= sh_prochk_minpid)
983 {
984 res[num - sh_prochk_minpid] |= flag;
985 }
986 line = SKIP_END;
987 break;
988 }
989 else
990 {
991 line = SKIP_TO_WS2;
992 break;
993 }
994 default:
995 SL_RETURN ((-1), _("sh_processes_readps"));
996 }
997 } while (1);
998
999 if (ferror(in))
1000 {
1001 SL_RETURN ((-1), _("sh_processes_readps"));
1002 }
1003
1004 SL_RETURN ((0), _("sh_processes_readps"));
1005}
1006
1007static int sh_processes_runps (short * res, char * str, size_t len,
1008 short flag, pid_t pid)
1009{
1010 sh_tas_t task;
1011
1012 int status = 0;
1013 char * p;
1014 struct sigaction new_act;
1015 struct sigaction old_act;
1016 int retval = 0;
1017 char dir[SH_PATHBUF];
1018
1019 SL_ENTER(_("sh_processes_runps"));
1020
1021 sh_ext_tas_init(&task);
1022 p = sh_unix_getUIDdir (SH_ERR_ERR, task.run_user_uid, dir, sizeof(dir));
1023 if (p)
1024 {
1025 (void) sh_ext_tas_add_envv (&task, _("HOME"), p);
1026 }
1027 (void) sh_ext_tas_add_envv (&task, _("SHELL"),
1028 _("/bin/sh"));
1029 (void) sh_ext_tas_add_envv (&task, _("PATH"),
1030 _("/sbin:/usr/sbin:/bin:/usr/bin"));
1031 if (sh.timezone != NULL)
1032 {
1033 (void) sh_ext_tas_add_envv(&task, "TZ", sh.timezone);
1034 }
1035
1036 if (!sh_prochk_pspath)
1037 sh_ext_tas_command(&task, PSPATH);
1038 else
1039 sh_ext_tas_command(&task, sh_prochk_pspath);
1040
1041 (void) sh_ext_tas_add_argv(&task, _("ps"));
1042
1043 if (!sh_prochk_psarg)
1044 {
1045#ifdef PS_THREADS
1046 (void) sh_ext_tas_add_argv(&task, _("-eT"));
1047#else
1048 (void) sh_ext_tas_add_argv(&task, PSARG);
1049#endif
1050 }
1051 else
1052 {
1053 (void) sh_ext_tas_add_argv(&task, sh_prochk_psarg);
1054 }
1055
1056 task.rw = 'r';
1057 task.fork_twice = S_FALSE;
1058
1059 status = sh_ext_popen(&task);
1060 if (status != 0)
1061 {
1062 /* SH_MUTEX_LOCK(mutex_thread_nolog) is in caller */
1063 sh_error_handle(SH_ERR_ALL, FIL__, __LINE__, status, MSG_E_SUBGEN,
1064 _("Could not open pipe"), _("sh_processes_runps"));
1065 SL_RETURN ((-1), _("sh_processes_runps"));
1066 }
1067
1068 /* ignore SIGPIPE (instead get EPIPE if connection is closed)
1069 */
1070 new_act.sa_handler = SIG_IGN;
1071 (void) retry_sigaction (FIL__, __LINE__, SIGPIPE, &new_act, &old_act);
1072
1073 /* read from the open pipe
1074 */
1075 if (task.pipe != NULL)
1076 {
1077 retval = sh_processes_readps (task.pipe, res, str, len, flag, pid);
1078 }
1079
1080 /* restore old signal handler
1081 */
1082 (void) retry_sigaction (FIL__, __LINE__, SIGPIPE, &old_act, NULL);
1083
1084 /* close pipe and return exit status
1085 */
1086 (void) sh_ext_pclose(&task);
1087 sh_ext_tas_free (&task);
1088 SL_RETURN ((retval), _("sh_processes_runps"));
1089}
1090
1091/* Check whether there is a visible process
1092 * with PID = i + 1024
1093 */
1094static size_t p_store = 0;
1095
1096static int openvz_ok(short * res, size_t i)
1097{
1098
1099 if (sh_prochk_openvz == S_FALSE) {
1100 return 0;
1101 }
1102
1103 i += 1024;
1104
1105 if (i >= sh_prochk_size) {
1106 return 0;
1107 }
1108
1109 if ( ((res[i] & SH_PR_PS) || (res[i] & SH_PR_PS2)) && (res[i] & SH_PR_ANY))
1110 {
1111 /* This is a system process corresponding to a 'virtual'
1112 * process that has a PID offset by 1024
1113 */
1114 return 1;
1115 }
1116
1117 if (openvz_hidden > 0)
1118 {
1119 p_store = i;
1120 --openvz_hidden;
1121 return 1;
1122 }
1123 else if (i == p_store)
1124 {
1125 return 1;
1126 }
1127
1128 return 0;
1129}
1130
1131static int sh_process_check_int (short * res)
1132{
1133 volatile size_t i;
1134 size_t j;
1135 char tests[512];
1136 volatile int retval;
1137
1138 pid_t this_pid;
1139
1140 SL_ENTER(_("sh_process_check_int"));
1141
1142 this_pid = getpid();
1143
1144 if (!res)
1145 {
1146 SH_MUTEX_LOCK(mutex_thread_nolog);
1147 sh_error_handle((-1), FIL__, __LINE__, 0, MSG_E_SUBGEN,
1148 _("Internal error: NULL argument, switching off"),
1149 _("sh_process_check_int"));
1150 SH_MUTEX_UNLOCK(mutex_thread_nolog);
1151 SL_RETURN ((-1), _("sh_process_check_int"));
1152 }
1153
1154 SH_MUTEX_LOCK(mutex_thread_nolog);
1155 retval = sh_processes_runps (res, NULL, 0, SH_PR_PS, 0);
1156 SH_MUTEX_UNLOCK(mutex_thread_nolog);
1157 for (i = sh_prochk_minpid; i != sh_prochk_maxpid; ++i)
1158 {
1159 j = i - sh_prochk_minpid;
1160 res[j] = sh_processes_check ((pid_t) i, res[j]);
1161 }
1162 SH_MUTEX_LOCK(mutex_thread_nolog);
1163 retval += sh_processes_runps (res, NULL, 0, SH_PR_PS2, 0);
1164 SH_MUTEX_UNLOCK(mutex_thread_nolog);
1165
1166 if (retval != 0)
1167 {
1168 SH_MUTEX_LOCK(mutex_thread_nolog);
1169 sh_error_handle((-1), FIL__, __LINE__, 0, MSG_E_SUBGEN,
1170 _("Failed to run ps, switching off"),
1171 _("sh_process_check_int"));
1172 SH_MUTEX_UNLOCK(mutex_thread_nolog);
1173 SL_RETURN ((-1), _("sh_process_check_int"));
1174 }
1175
1176 /* Evaluate results
1177 */
1178 for (i = sh_prochk_minpid; i != sh_prochk_maxpid; ++i)
1179 {
1180 /* don't check the current process
1181 */
1182 if (i == (size_t) this_pid)
1183 continue;
1184
1185 j = i - sh_prochk_minpid;
1186
1187 if (((res[j] & SH_PR_PS) != 0) || ((res[j] & SH_PR_PS2) != 0))
1188 {
1189 res[j] |= SH_PR_PS_ANY;
1190 }
1191 else
1192 {
1193 res[j] &= ~SH_PR_PS_ANY;
1194 }
1195
1196 tests[0] = '\0';
1197
1198 if ((res[j] & SH_PR_ANY) || (res[j] & SH_PR_PS_ANY))
1199 {
1200 /* list all tests where the pid was found
1201 */
1202 sh_processes_tlist (tests, sizeof(tests), res[j]);
1203
1204 /*
1205 * case 1: in ps and found
1206 */
1207 if ((res[j] & SH_PR_PS_ANY) && (res[j] & SH_PR_ANY))
1208 {
1209 SH_MUTEX_LOCK(mutex_thread_nolog);
1210 sh_error_handle((-1), FIL__, __LINE__, 0, MSG_PCK_OK,
1211 (unsigned long) i, tests);
1212 SH_MUTEX_UNLOCK(mutex_thread_nolog);
1213 }
1214
1215 /*
1216 * case 2: not in ps and found
1217 */
1218 else if ((res[j] & SH_PR_PS_ANY) == 0)
1219 {
1220 res[j] = sh_processes_check ((pid_t) i, 0);
1221 /*
1222 * if still there, it is real and hidden
1223 */
1224 if ((res[j] & SH_PR_ANY) && !openvz_ok(res, j))
1225 {
1226 if (S_FALSE == is_in_list(&list_hidden, NULL, i))
1227 {
1228 char user[16];
1229 char * aout;
1230 char * safe;
1231
1232 SH_MUTEX_LOCK(mutex_thread_nolog);
1233 aout = get_user_and_path ((pid_t) i, user, sizeof(user));
1234 SH_MUTEX_UNLOCK(mutex_thread_nolog);
1235
1236 if (aout)
1237 {
1238 safe = sh_util_safe_name (aout);
1239 SH_MUTEX_LOCK(mutex_thread_nolog);
1240 sh_error_handle(sh_prochk_severity, FIL__, __LINE__, 0,
1241 MSG_PCK_P_HIDDEN,
1242 (unsigned long) i, tests, safe, user);
1243 SH_MUTEX_UNLOCK(mutex_thread_nolog);
1244 SH_FREE(safe);
1245 SH_FREE(aout);
1246 }
1247 else
1248 {
1249 SH_MUTEX_LOCK(mutex_thread_nolog);
1250 sh_error_handle(sh_prochk_severity, FIL__, __LINE__, 0,
1251 MSG_PCK_HIDDEN,
1252 (unsigned long) i, tests);
1253 SH_MUTEX_UNLOCK(mutex_thread_nolog);
1254 }
1255 }
1256 }
1257 }
1258
1259 /*
1260 * case 3: in ps, but not found
1261 */
1262 else
1263 {
1264 if (((res[j] & SH_PR_PS) != 0) && ((res[j] & SH_PR_PS2) != 0))
1265 {
1266 if (S_FALSE == is_in_list(&list_fake, NULL, i))
1267 {
1268 SH_MUTEX_LOCK(mutex_thread_nolog);
1269 sh_error_handle(sh_prochk_severity, FIL__, __LINE__, 0,
1270 MSG_PCK_FAKE,
1271 (unsigned long) i, tests);
1272 SH_MUTEX_UNLOCK(mutex_thread_nolog);
1273 }
1274 }
1275 }
1276 }
1277 } /* loop end */
1278
1279 check_watchlist (res);
1280
1281 SL_RETURN (0, _("sh_process_check_int"));
1282}
1283
1284/* Initialise.
1285 */
1286static int sh_prochk_init_internal(void)
1287{
1288 SL_ENTER(_("sh_prochk_init"));
1289
1290 (void) proc_max_pid (&sh_prochk_maxpid);
1291
1292 if (sh_prochk_minpid > sh_prochk_maxpid)
1293 ShProchkActive = S_FALSE;
1294
1295 /* We need to free anything allocated by the configuration functions if
1296 * we find that the module is to be left inactive - otherwise _reconf()
1297 * won't quite work.
1298 */
1299 if( ShProchkActive == S_FALSE )
1300 {
1301 sh_prochk_free_list(process_check);
1302 process_check = NULL;
1303 SL_RETURN(-1, _("sh_prochk_init"));
1304 }
1305
1306 sh_prochk_size = sh_prochk_maxpid - sh_prochk_minpid;
1307
1308 sh_prochk_res = SH_ALLOC(sizeof(short) * sh_prochk_size);
1309 memset (sh_prochk_res, 0, sizeof(short) * sh_prochk_size);
1310
1311 SL_RETURN(0, _("sh_prochk_init"));
1312}
1313
1314int sh_prochk_init (struct mod_type * arg)
1315{
1316#ifndef HAVE_PTHREAD
1317 (void) arg;
1318#endif
1319
1320 if (ShProchkActive == S_FALSE)
1321 return SH_MOD_FAILED;
1322#ifdef HAVE_PTHREAD
1323 if (arg != NULL && arg->initval < 0 &&
1324 (sh.flag.isdaemon == S_TRUE || sh.flag.loop == S_TRUE))
1325 {
1326 if (0 == sh_pthread_create(sh_threaded_module_run, (void *)arg))
1327 return SH_MOD_THREAD;
1328 else
1329 return SH_MOD_FAILED;
1330 }
1331#endif
1332 return sh_prochk_init_internal();
1333}
1334
1335int sh_prochk_timer(time_t tcurrent)
1336{
1337 static time_t lastcheck = 0;
1338
1339 SL_ENTER(_("sh_prochk_timer"));
1340 if ((time_t) (tcurrent - lastcheck) >= sh_prochk_interval)
1341 {
1342 lastcheck = tcurrent;
1343 SL_RETURN((-1), _("sh_prochk_timer"));
1344 }
1345 SL_RETURN(0, _("sh_prochk_timer"));
1346}
1347
1348int sh_prochk_check(void)
1349{
1350 int status;
1351
1352 SL_ENTER(_("sh_prochk_check"));
1353
1354 SH_MUTEX_LOCK(mutex_proc_check);
1355
1356 status = 0;
1357
1358 if( ShProchkActive != S_FALSE )
1359 {
1360 SH_MUTEX_LOCK(mutex_thread_nolog);
1361 sh_error_handle((-1), FIL__, __LINE__, 0, MSG_PCK_CHECK,
1362 (unsigned long) sh_prochk_minpid,
1363 (unsigned long) (sh_prochk_maxpid-1));
1364 SH_MUTEX_UNLOCK(mutex_thread_nolog);
1365
1366 if (sh_prochk_res) {
1367 memset (sh_prochk_res, 0, sizeof(short) * sh_prochk_size);
1368 }
1369 status = sh_process_check_int(sh_prochk_res);
1370
1371 if (status != 0)
1372 ShProchkActive = S_FALSE;
1373
1374 /* clean out old entries which are not marked
1375 * as missing/hidden/fake anymore
1376 */
1377 clean_list (&list_missing);
1378 clean_list (&list_hidden);
1379 clean_list (&list_fake);
1380 }
1381
1382 SH_MUTEX_UNLOCK(mutex_proc_check);
1383
1384 SL_RETURN(status, _("sh_prochk_check"));
1385}
1386
1387/* Free our lists and the associated memory
1388 */
1389int sh_prochk_cleanup(void)
1390{
1391 SL_ENTER(_("sh_prochk_cleanup"));
1392
1393 sh_prochk_reconf();
1394
1395 if (list_missing) {
1396 kill_list(list_missing);
1397 list_missing = NULL;
1398 }
1399 if (list_hidden) {
1400 kill_list(list_hidden);
1401 list_hidden = NULL;
1402 }
1403 if (list_fake) {
1404 kill_list(list_fake);
1405 list_fake = NULL;
1406 }
1407
1408 SL_RETURN(0, _("sh_prochk_cleanup"));
1409}
1410
1411/* Free our lists and the associated memory
1412 */
1413int sh_prochk_reconf(void)
1414{
1415 SL_ENTER(_("sh_prochk_reconf"));
1416
1417 SH_MUTEX_LOCK(mutex_proc_check);
1418 userdef_maxpid = 0;
1419 sh_prochk_maxpid = 0x8000;
1420 sh_prochk_minpid = 0x0001;
1421 sh_prochk_interval = SH_PROCHK_INTERVAL;
1422 sh_prochk_openvz = S_FALSE;
1423 p_store = 0;
1424 openvz_hidden = 0;
1425
1426 sh_prochk_free_list(process_check);
1427 process_check = NULL;
1428 if (sh_prochk_res != NULL)
1429 SH_FREE(sh_prochk_res);
1430 sh_prochk_res = NULL;
1431
1432 if (sh_prochk_psarg)
1433 SH_FREE(sh_prochk_psarg);
1434 sh_prochk_psarg = NULL;
1435 if (sh_prochk_pspath)
1436 SH_FREE(sh_prochk_pspath);
1437 sh_prochk_pspath = NULL;
1438 SH_MUTEX_UNLOCK(mutex_proc_check);
1439
1440 SL_RETURN(0, _("sh_prochk_reconf"));
1441}
1442
1443/* #if defined (SH_WITH_CLIENT) || defined (SH_STANDALONE) */
1444#endif
1445
1446/* #ifdef SH_USE_PROCESSCHECK */
1447#endif
1448
1449
1450#ifdef SH_CUTEST
1451#include "CuTest.h"
1452
1453void Test_processcheck_watchlist_ok (CuTest *tc) {
1454#if defined(SH_USE_PROCESSCHECK) && (defined(SH_WITH_CLIENT) || defined(SH_STANDALONE))
1455 CuAssertTrue(tc, 0 == sh_prochk_add_process("init"));
1456 CuAssertTrue(tc,
1457 S_TRUE == is_in_watchlist(" 1 ? 00:00:00 init", 0));
1458 CuAssertTrue(tc,
1459 S_FALSE == is_in_watchlist(" 1 ? 00:00:00 flix", 0));
1460 CuAssertTrue(tc,
1461 S_TRUE == is_in_watchlist("25218 ? SNs 0:01 /usr/sbin/init -k start -DSSL", 0));
1462 CuAssertTrue(tc,
1463 S_FALSE == is_in_watchlist("25218 ? SNs 0:01 /usr/sbin/apache2 -k start -DSSL", 0));
1464
1465
1466 sh_prochk_free_list(process_check);
1467 process_check = NULL;
1468 CuAssertTrue(tc, S_FALSE == is_in_watchlist("init", 0));
1469
1470 CuAssertTrue(tc, 0 == sh_prochk_add_process("init"));
1471 CuAssertTrue(tc, 0 == sh_prochk_add_process("ssh"));
1472 CuAssertTrue(tc, 0 == sh_prochk_add_process("syslog"));
1473 CuAssertTrue(tc, S_TRUE == is_in_watchlist("init", 0));
1474 CuAssertTrue(tc, S_TRUE == is_in_watchlist("ssh", 0));
1475 CuAssertTrue(tc, S_TRUE == is_in_watchlist("syslog", 0));
1476
1477 sh_prochk_free_list(process_check);
1478 process_check = NULL;
1479 CuAssertTrue(tc, S_FALSE == is_in_watchlist("init", 0));
1480 CuAssertTrue(tc, S_FALSE == is_in_watchlist("ssh", 0));
1481 CuAssertTrue(tc, S_FALSE == is_in_watchlist("syslog", 0));
1482#else
1483 (void) tc; /* fix compiler warning */
1484#endif
1485 return;
1486}
1487
1488void Test_processcheck_listhandle_ok (CuTest *tc) {
1489#if defined(SH_USE_PROCESSCHECK) && (defined(SH_WITH_CLIENT) || defined(SH_STANDALONE))
1490 CuAssertTrue(tc, S_FALSE == is_in_list(&list_missing, "init", 0));
1491 CuAssertTrue(tc, S_TRUE == is_in_list(&list_missing, "init", 0));
1492 CuAssertTrue(tc, S_FALSE == is_in_list(&list_missing, "foobar", 0));
1493 CuAssertTrue(tc, S_TRUE == is_in_list(&list_missing, "foobar", 0));
1494
1495 if (list_missing)
1496 kill_list(list_missing);
1497 list_missing = NULL;
1498
1499 CuAssertTrue(tc, S_FALSE == is_in_list(&list_missing, "init", 0));
1500 CuAssertTrue(tc, S_TRUE == is_in_list(&list_missing, "init", 0));
1501 CuAssertTrue(tc, S_FALSE == is_in_list(&list_missing, "foobar", 0));
1502 CuAssertTrue(tc, S_TRUE == is_in_list(&list_missing, "foobar", 0));
1503
1504 if (list_missing)
1505 kill_list(list_missing);
1506 list_missing = NULL;
1507
1508 CuAssertTrue(tc, S_FALSE == is_in_list(&list_missing, "init", 0));
1509 CuAssertTrue(tc, S_TRUE == is_in_list(&list_missing, "init", 0));
1510 CuAssertTrue(tc, S_FALSE == is_in_list(&list_missing, "foobar", 0));
1511 CuAssertTrue(tc, S_TRUE == is_in_list(&list_missing, "foobar", 0));
1512
1513 CuAssertTrue(tc, 2 == clean_list(&list_missing));
1514 CuAssertPtrNotNull(tc, list_missing);
1515
1516 CuAssertTrue(tc, S_TRUE == is_in_list(&list_missing, "init", 0));
1517 CuAssertTrue(tc, S_TRUE == is_in_list(&list_missing, "foobar", 0));
1518
1519 CuAssertTrue(tc, 2 == clean_list(&list_missing));
1520 CuAssertPtrNotNull(tc, list_missing);
1521
1522 CuAssertTrue(tc, 0 == clean_list(&list_missing));
1523 CuAssertTrue(tc, NULL == list_missing);
1524#else
1525 (void) tc; /* fix compiler warning */
1526#endif
1527 return;
1528}
1529
1530
1531/* #ifdef SH_CUTEST */
1532#endif
1533
Note: See TracBrowser for help on using the repository browser.