source: trunk/src/sh_processcheck.c@ 252

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

Add code to check for stale file records on close() and fclose(), fix sl_close() to handle open stream (ticket #163).

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_FREE(tmp);
433 SH_MUTEX_UNLOCK(mutex_thread_nolog);
434 }
435 }
436 else
437 {
438 indx = list->pid - sh_prochk_minpid;
439
440 if (list->pid < sh_prochk_maxpid && list->pid >= sh_prochk_minpid &&
441 ((res[indx] & SH_PR_ANY) == 0) && /* not found */
442 ((res[indx] & SH_PR_PS) != 0) && /* seen in first ps */
443 ((res[indx] & SH_PR_PS2) != 0)) /* seen in second ps */
444 {
445 /* fake process, thus considered missing
446 */
447 if (S_FALSE == is_in_list(&list_missing, list->str, 0))
448 {
449 SH_MUTEX_LOCK(mutex_thread_nolog);
450 tmp = sh_util_safe_name (list->str);
451 sh_error_handle(sh_prochk_severity, FIL__, __LINE__, 0,
452 MSG_PCK_MISS,
453 tmp);
454 SH_FREE(tmp);
455 SH_MUTEX_UNLOCK(mutex_thread_nolog);
456 }
457 }
458 list->seen = S_FALSE;
459 }
460 list = list->next;
461 }
462}
463
464/* Add 'str' to the list of watched processes for which
465 * existence should be checked.
466 */
467int sh_prochk_add_process (const char *str)
468{
469 struct watchlist *new;
470 int status;
471 char errbuf[256];
472
473 SL_ENTER(_("sh_prochk_add_process"));
474
475 if( str == NULL )
476 SL_RETURN(-1, _("sh_prochk_add_process") );
477
478 new = SH_ALLOC(sizeof(struct watchlist));
479 new->next = process_check;
480 new->str = sh_util_strdup(str);
481#ifdef HAVE_REGEX_H
482 status = regcomp(&(new->preg), str, REG_NOSUB|REG_EXTENDED);
483 if (status != 0)
484 {
485 regerror(status, &(new->preg), errbuf, sizeof(errbuf));
486 SH_MUTEX_LOCK(mutex_thread_nolog);
487 sh_error_handle((-1), FIL__, __LINE__, status, MSG_E_SUBGEN,
488 errbuf, _("sh_processes_add_process"));
489 SH_MUTEX_UNLOCK(mutex_thread_nolog);
490 SH_FREE(new->str);
491 SH_FREE(new);
492 SL_RETURN(-1, _("sh_prochk_add_process") );
493 }
494#endif
495 new->pid = 0;
496 new->seen = S_FALSE;
497
498 process_check = new;
499 SL_RETURN(0, _("sh_prochk_add_process") );
500}
501
502/* severity
503 */
504int sh_prochk_set_severity (const char * c)
505{
506 char tmp[32];
507 tmp[0] = '='; tmp[1] = '\0';
508 sl_strlcat (tmp, c, 32);
509 return sh_error_set_level (tmp, &sh_prochk_severity);
510}
511
512
513
514/* Path to ps
515 */
516int sh_prochk_set_pspath(const char *str)
517{
518 SL_ENTER(_("sh_prochk_set_pspath"));
519
520 if (!str && ('/' != str[0]))
521 SL_RETURN((-1), _("sh_prochk_set_pspath"));
522 if (sh_prochk_pspath)
523 SH_FREE(sh_prochk_pspath);
524 sh_prochk_pspath = sh_util_strdup (str);
525
526 SL_RETURN((0), _("sh_prochk_set_pspath"));
527
528}
529
530/* argument for ps
531 */
532int sh_prochk_set_psarg(const char *str)
533{
534 SL_ENTER(_("sh_prochk_set_psarg"));
535
536 if (sh_prochk_psarg)
537 SH_FREE(sh_prochk_psarg);
538 sh_prochk_psarg = sh_util_strdup (str);
539
540 SL_RETURN((0), _("sh_prochk_set_psarg"));
541
542}
543
544
545/* Decide if we're active.
546 */
547int sh_prochk_set_active(const char *str)
548{
549 int value;
550
551 SL_ENTER(_("sh_prochk_set_active"));
552
553 value = sh_util_flagval(str, &ShProchkActive);
554
555 SL_RETURN((value), _("sh_prochk_set_active"));
556}
557
558/* Are we on openvz.
559 */
560static int openvz_hidden = 0;
561
562int sh_prochk_set_openvz(const char *str)
563{
564 int value;
565
566 SL_ENTER(_("sh_prochk_set_openvz"));
567
568 value = sh_util_flagval(str, &sh_prochk_openvz);
569
570 if (sh_prochk_openvz != S_FALSE) {
571 openvz_hidden = 1;
572 }
573
574 SL_RETURN((value), _("sh_prochk_set_openvz"));
575}
576
577/* Minimum PID
578 */
579int sh_prochk_set_minpid(const char * str)
580{
581 size_t value;
582 char * foo;
583 int retval = 0;
584
585 SL_ENTER(_("sh_prochk_set_minpid"));
586
587 value = (size_t) strtoul(str, &foo, 0);
588 if (*foo != '\0')
589 retval = -1;
590 else
591 sh_prochk_minpid = value;
592
593 SL_RETURN((retval), _("sh_prochk_set_minpid"));
594}
595
596/* Maximum PID
597 */
598static int userdef_maxpid = 0;
599
600int sh_prochk_set_maxpid(const char * str)
601{
602 size_t value;
603 char * foo;
604 int retval = -1;
605
606 SL_ENTER(_("sh_prochk_set_maxpid"));
607
608 value = (size_t) strtoul(str, &foo, 0);
609
610 if (*foo == '\0' && SL_TRUE == sl_ok_adds(value, 1)) {
611 sh_prochk_maxpid = value + 1;
612 userdef_maxpid = 1;
613 }
614
615 SL_RETURN((retval), _("sh_prochk_set_maxpid"));
616}
617
618int sh_prochk_set_interval (const char * c)
619{
620 int retval = 0;
621 long val;
622
623 SL_ENTER(_("sh_prochk_set_interval"));
624 val = strtol (c, (char **)NULL, 10);
625 if (val <= 0)
626 {
627 SH_MUTEX_LOCK(mutex_thread_nolog);
628 sh_error_handle ((-1), FIL__, __LINE__, EINVAL, MSG_EINVALS,
629 _("process check interval"), c);
630 SH_MUTEX_UNLOCK(mutex_thread_nolog);
631 retval = -1;
632 }
633
634 sh_prochk_interval = (time_t) val;
635 SL_RETURN(0, _("sh_prochk_set_interval"));
636}
637
638
639
640/* Recurse to the end of the list and then free the data as we return
641 * back up towards the start, making sure to free any strdupped strings
642 */
643static void sh_prochk_free_list(struct watchlist *head)
644{
645 if ( head != NULL )
646 {
647 sh_prochk_free_list(head->next);
648 if (head->str)
649 SH_FREE(head->str);
650#ifdef HAVE_REGEX_H
651 regfree(&(head->preg));
652#endif
653 SH_FREE(head);
654 }
655 return;
656}
657
658#if defined(__linux__)
659#define PROC_PID_MAX _("/proc/sys/kernel/pid_max")
660
661static int proc_max_pid (size_t * procpid)
662{
663 char * ret;
664 unsigned long pid;
665 FILE * fd;
666 char str[128];
667 char * ptr;
668
669 SL_ENTER(_("proc_max_pid"));
670
671 if (userdef_maxpid != 0)
672 SL_RETURN((-1), _("proc_max_pid"));
673
674 if (0 == access(PROC_PID_MAX, R_OK)) /* flawfinder: ignore */
675 {
676 if (NULL != (fd = fopen(PROC_PID_MAX, "r")))
677 {
678 str[0] = '\0';
679 ret = fgets(str, 128, fd);
680 if (ret && *str != '\0')
681 {
682 pid = strtoul(str, &ptr, 0);
683 if (*ptr == '\0' || *ptr == '\n')
684 {
685 sl_fclose(FIL__, __LINE__, fd);
686 *procpid = (size_t) pid;
687 SL_RETURN(0, _("proc_max_pid"));
688 }
689 }
690 sl_fclose(FIL__, __LINE__, fd);
691 }
692 }
693 SL_RETURN((-1), _("proc_max_pid"));
694}
695#else
696static int proc_max_pid(size_t * dummy)
697{
698 (void) dummy;
699 return -1;
700}
701#endif
702
703static void sh_processes_tlist (char * list, size_t len, short res)
704{
705 if (res & SH_PR_PS) sl_strlcat(list, _(" ps(initial)"), len);
706 if (res & SH_PR_CHDIR) sl_strlcat(list, _(" chdir"), len);
707 if (res & SH_PR_OPENDIR) sl_strlcat(list, _(" opendir"), len);
708 if (res & SH_PR_LSTAT) sl_strlcat(list, _(" lstat"), len);
709 if (res & SH_PR_PRIORITY) sl_strlcat(list, _(" getpriority"), len);
710 if (res & SH_PR_SCHED) sl_strlcat(list, _(" sched_getparam"), len);
711 if (res & SH_PR_GETSID) sl_strlcat(list, _(" getsid"), len);
712 if (res & SH_PR_GETPGID) sl_strlcat(list, _(" getpgid"), len);
713 if (res & SH_PR_KILL) sl_strlcat(list, _(" kill"), len);
714 if (res & SH_PR_STATVSF) sl_strlcat(list, _(" statvfs"), len);
715 if (res & SH_PR_PS2) sl_strlcat(list, _(" ps(final)"), len);
716 return;
717}
718
719
720static short sh_processes_check (pid_t pid, short res)
721{
722 int have_checks = 0;
723 int need_checks = 0;
724#ifdef HAVE_PROCFS
725 char path[128];
726 struct stat buf;
727 DIR * dir;
728 int retval;
729#ifdef HAVE_STATVFS
730 struct statvfs vfsbuf;
731#endif
732#endif
733
734#if !defined(sun) && !defined(__sun) && !defined(__sun__)
735#ifdef _POSIX_PRIORITY_SCHEDULING
736 struct sched_param p;
737#endif
738#endif
739
740 if (0 == kill(pid, 0))
741 {
742 res |= SH_PR_KILL; res |= SH_PR_ANY; ++have_checks;
743 ++need_checks;
744 }
745 else if (errno != EPERM)
746 {
747 ++need_checks;
748 }
749
750
751#ifdef HAVE_GETPGID
752 if ((pid_t)-1 != getpgid(pid))
753 {
754 res |= SH_PR_GETPGID; res |= SH_PR_ANY; ++have_checks;
755 }
756 ++need_checks;
757#endif
758
759#ifdef HAVE_GETSID
760 if ((pid_t)-1 != getsid(pid))
761 {
762 res |= SH_PR_GETSID; res |= SH_PR_ANY; ++have_checks;
763 }
764 ++need_checks;
765#endif
766
767 /* sched_getparam() is broken on solaris 10, may segfault in librt
768 */
769#if !defined(sun) && !defined(__sun) && !defined(__sun__)
770#ifdef _POSIX_PRIORITY_SCHEDULING
771 if (0 == sched_getparam (pid, &p))
772 {
773 res |= SH_PR_SCHED; res |= SH_PR_ANY; ++have_checks;
774 }
775 ++need_checks;
776#endif
777#endif
778
779#ifdef HAVE_GETPRIORITY
780 errno = 0;
781 if (((-1) == getpriority (PRIO_PROCESS, (int) pid)) && (errno == ESRCH));
782 else
783 {
784 res |= SH_PR_PRIORITY; res |= SH_PR_ANY; ++have_checks;
785 }
786 ++need_checks;
787#endif
788
789#ifdef HAVE_PROCFS
790 sl_snprintf (path, sizeof(path), "/proc/%ld", (unsigned long) pid);
791
792 do {
793 retval = lstat (path, &buf);
794 } while (retval < 0 && errno == EINTR);
795
796 if (0 == retval)
797 {
798 res |= SH_PR_LSTAT; res |= SH_PR_ANY; ++have_checks;
799 }
800 ++need_checks;
801
802 if (NULL != (dir = opendir(path)))
803 {
804 res |= SH_PR_OPENDIR; res |= SH_PR_ANY; ++have_checks;
805 closedir(dir);
806 }
807 ++need_checks;
808
809#ifdef HAVE_STATVFS
810 do {
811 retval = statvfs (path, &vfsbuf);
812 } while (retval < 0 && errno == EINTR);
813
814 if (0 == retval)
815 {
816 res |= SH_PR_STATVSF; res |= SH_PR_ANY; ++have_checks;
817 }
818 ++need_checks;
819#endif
820
821#if !defined(SH_PROFILE)
822 if (0 == chdir(path))
823 {
824 res |= SH_PR_CHDIR; res |= SH_PR_ANY; ++have_checks;
825 do {
826 retval = chdir ("/");
827 } while (retval < 0 && errno == EINTR);
828 }
829 ++need_checks;
830#endif
831#endif
832
833 if (have_checks == need_checks)
834 {
835 res |= SH_PR_ALL;
836 }
837 return res;
838}
839
840extern int flag_err_debug;
841
842static int sh_processes_readps (FILE * in, short * res,
843 char * str, size_t len,
844 short flag, pid_t pid)
845{
846 int cc;
847 unsigned int lnum = 0;
848 unsigned long num = 0;
849 char c;
850 unsigned int pos = 0;
851 char tstr[256];
852 enum { SKIP_TO_WS, SKIP_WS, SKIP_TO_WS2, SKIP_WS2, GET_NUM, SKIP_END, GET_NUM2 } line;
853
854 SL_ENTER(_("sh_processes_readps"));
855
856 if (!in) {
857 SL_RETURN((-1), _("sh_processes_readps"));
858 }
859
860 tstr[(sizeof(tstr)-1)] = '\0';
861 tstr[0] = '\0';
862 line = SKIP_END; /* Skip 1st line */
863
864 do
865 {
866 cc = fgetc(in);
867
868 if (EOF == cc)
869 {
870 if (feof(in))
871 {
872 break;
873 }
874 else if (errno == EAGAIN)
875 {
876 clearerr(in);
877 continue;
878 }
879#ifdef HOST_IS_OPENBSD
880 else if (errno == ENODEV)
881 {
882 clearerr(in);
883 continue;
884 }
885#endif
886 else
887 {
888 char errbuf[SH_ERRBUF_SIZE];
889
890 /* SH_MUTEX_LOCK(mutex_thread_nolog) is in caller */
891 sh_error_handle(SH_ERR_ALL, FIL__, __LINE__, errno, MSG_E_SUBGEN,
892 sh_error_message(errno, errbuf, sizeof(errbuf)),
893 _("sh_processes_readps"));
894 break;
895 }
896 }
897
898 c = (char) cc;
899
900 if (pos < (sizeof(tstr)-1))
901 {
902 tstr[pos] = c; ++pos;
903 }
904
905 switch(line)
906 {
907 case SKIP_END:
908 if (c == '\n')
909 {
910 tstr[pos-1] = '\0';
911 if (flag_err_debug == SL_TRUE)
912 {
913 /* SH_MUTEX_LOCK(mutex_thread_nolog) is in caller */
914 sh_error_handle(SH_ERR_ALL, FIL__, __LINE__, num,
915 MSG_E_SUBGEN,
916 tstr,
917 _("sh_processes_readps"));
918 }
919 /* fprintf(stderr, "<%ld> %s\n", num, tstr); */
920 line = SKIP_WS; pos = 0;
921 if (str != NULL && num == (unsigned long) pid)
922 sl_strlcpy(str, tstr, len);
923 if (lnum != 0)
924 is_in_watchlist (tstr, num);
925 ++lnum;
926 }
927 break;
928 case SKIP_TO_WS:
929 if (!isspace(cc))
930 break;
931 line = SKIP_WS;
932 /* fallthrough */
933 case SKIP_WS:
934 if (isspace(cc))
935 break;
936 num = 0;
937 line = GET_NUM;
938 /* fallthrough */
939 case GET_NUM:
940 if (isdigit(cc))
941 {
942 num = num * 10 + (c - '0');
943 break;
944 }
945 else if (isspace(cc))
946 {
947#ifdef PS_THREADS
948 num = 0;
949 line = SKIP_WS2;
950#else
951 if (num < sh_prochk_maxpid && num >= sh_prochk_minpid)
952 {
953 res[num - sh_prochk_minpid] |= flag;
954 }
955 line = SKIP_END;
956#endif
957 break;
958 }
959 else
960 {
961 line = SKIP_TO_WS;
962 break;
963 }
964 case SKIP_TO_WS2:
965 if (!isspace(cc))
966 break;
967 line = SKIP_WS2;
968 /* fallthrough */
969 case SKIP_WS2:
970 if (isspace(cc))
971 break;
972 num = 0;
973 line = GET_NUM2;
974 /* fallthrough */
975 case GET_NUM2:
976 if (isdigit(cc))
977 {
978 num = num * 10 + (c - '0');
979 break;
980 }
981 else if (isspace(cc))
982 {
983 if (num < sh_prochk_maxpid && num >= sh_prochk_minpid)
984 {
985 res[num - sh_prochk_minpid] |= flag;
986 }
987 line = SKIP_END;
988 break;
989 }
990 else
991 {
992 line = SKIP_TO_WS2;
993 break;
994 }
995 default:
996 SL_RETURN ((-1), _("sh_processes_readps"));
997 }
998 } while (1);
999
1000 if (ferror(in))
1001 {
1002 SL_RETURN ((-1), _("sh_processes_readps"));
1003 }
1004
1005 SL_RETURN ((0), _("sh_processes_readps"));
1006}
1007
1008static int sh_processes_runps (short * res, char * str, size_t len,
1009 short flag, pid_t pid)
1010{
1011 sh_tas_t task;
1012
1013 int status = 0;
1014 char * p;
1015 struct sigaction new_act;
1016 struct sigaction old_act;
1017 int retval = 0;
1018 char dir[SH_PATHBUF];
1019
1020 SL_ENTER(_("sh_processes_runps"));
1021
1022 sh_ext_tas_init(&task);
1023 p = sh_unix_getUIDdir (SH_ERR_ERR, task.run_user_uid, dir, sizeof(dir));
1024 if (p)
1025 {
1026 (void) sh_ext_tas_add_envv (&task, _("HOME"), p);
1027 }
1028 (void) sh_ext_tas_add_envv (&task, _("SHELL"),
1029 _("/bin/sh"));
1030 (void) sh_ext_tas_add_envv (&task, _("PATH"),
1031 _("/sbin:/usr/sbin:/bin:/usr/bin"));
1032 if (sh.timezone != NULL)
1033 {
1034 (void) sh_ext_tas_add_envv(&task, "TZ", sh.timezone);
1035 }
1036
1037 if (!sh_prochk_pspath)
1038 sh_ext_tas_command(&task, PSPATH);
1039 else
1040 sh_ext_tas_command(&task, sh_prochk_pspath);
1041
1042 (void) sh_ext_tas_add_argv(&task, _("ps"));
1043
1044 if (!sh_prochk_psarg)
1045 {
1046#ifdef PS_THREADS
1047 (void) sh_ext_tas_add_argv(&task, _("-eT"));
1048#else
1049 (void) sh_ext_tas_add_argv(&task, PSARG);
1050#endif
1051 }
1052 else
1053 {
1054 (void) sh_ext_tas_add_argv(&task, sh_prochk_psarg);
1055 }
1056
1057 task.rw = 'r';
1058 task.fork_twice = S_FALSE;
1059
1060 status = sh_ext_popen(&task);
1061 if (status != 0)
1062 {
1063 /* SH_MUTEX_LOCK(mutex_thread_nolog) is in caller */
1064 sh_error_handle(SH_ERR_ALL, FIL__, __LINE__, status, MSG_E_SUBGEN,
1065 _("Could not open pipe"), _("sh_processes_runps"));
1066 SL_RETURN ((-1), _("sh_processes_runps"));
1067 }
1068
1069 /* ignore SIGPIPE (instead get EPIPE if connection is closed)
1070 */
1071 new_act.sa_handler = SIG_IGN;
1072 (void) retry_sigaction (FIL__, __LINE__, SIGPIPE, &new_act, &old_act);
1073
1074 /* read from the open pipe
1075 */
1076 if (task.pipe != NULL)
1077 {
1078 retval = sh_processes_readps (task.pipe, res, str, len, flag, pid);
1079 }
1080
1081 /* restore old signal handler
1082 */
1083 (void) retry_sigaction (FIL__, __LINE__, SIGPIPE, &old_act, NULL);
1084
1085 /* close pipe and return exit status
1086 */
1087 (void) sh_ext_pclose(&task);
1088 sh_ext_tas_free (&task);
1089 SL_RETURN ((retval), _("sh_processes_runps"));
1090}
1091
1092/* Check whether there is a visible process
1093 * with PID = i + 1024
1094 */
1095static size_t p_store = 0;
1096
1097static int openvz_ok(short * res, size_t i)
1098{
1099
1100 if (sh_prochk_openvz == S_FALSE) {
1101 return 0;
1102 }
1103
1104 i += 1024;
1105
1106 if (i >= sh_prochk_size) {
1107 return 0;
1108 }
1109
1110 if ( ((res[i] & SH_PR_PS) || (res[i] & SH_PR_PS2)) && (res[i] & SH_PR_ANY))
1111 {
1112 /* This is a system process corresponding to a 'virtual'
1113 * process that has a PID offset by 1024
1114 */
1115 return 1;
1116 }
1117
1118 if (openvz_hidden > 0)
1119 {
1120 p_store = i;
1121 --openvz_hidden;
1122 return 1;
1123 }
1124 else if (i == p_store)
1125 {
1126 return 1;
1127 }
1128
1129 return 0;
1130}
1131
1132static int sh_process_check_int (short * res)
1133{
1134 volatile size_t i;
1135 size_t j;
1136 char tests[512];
1137 volatile int retval;
1138
1139 pid_t this_pid;
1140
1141 SL_ENTER(_("sh_process_check_int"));
1142
1143 this_pid = getpid();
1144
1145 if (!res)
1146 {
1147 SH_MUTEX_LOCK(mutex_thread_nolog);
1148 sh_error_handle((-1), FIL__, __LINE__, 0, MSG_E_SUBGEN,
1149 _("Internal error: NULL argument, switching off"),
1150 _("sh_process_check_int"));
1151 SH_MUTEX_UNLOCK(mutex_thread_nolog);
1152 SL_RETURN ((-1), _("sh_process_check_int"));
1153 }
1154
1155 SH_MUTEX_LOCK(mutex_thread_nolog);
1156 retval = sh_processes_runps (res, NULL, 0, SH_PR_PS, 0);
1157 SH_MUTEX_UNLOCK(mutex_thread_nolog);
1158 for (i = sh_prochk_minpid; i != sh_prochk_maxpid; ++i)
1159 {
1160 j = i - sh_prochk_minpid;
1161 res[j] = sh_processes_check ((pid_t) i, res[j]);
1162 }
1163 SH_MUTEX_LOCK(mutex_thread_nolog);
1164 retval += sh_processes_runps (res, NULL, 0, SH_PR_PS2, 0);
1165 SH_MUTEX_UNLOCK(mutex_thread_nolog);
1166
1167 if (retval != 0)
1168 {
1169 SH_MUTEX_LOCK(mutex_thread_nolog);
1170 sh_error_handle((-1), FIL__, __LINE__, 0, MSG_E_SUBGEN,
1171 _("Failed to run ps, switching off"),
1172 _("sh_process_check_int"));
1173 SH_MUTEX_UNLOCK(mutex_thread_nolog);
1174 SL_RETURN ((-1), _("sh_process_check_int"));
1175 }
1176
1177 /* Evaluate results
1178 */
1179 for (i = sh_prochk_minpid; i != sh_prochk_maxpid; ++i)
1180 {
1181 /* don't check the current process
1182 */
1183 if (i == (size_t) this_pid)
1184 continue;
1185
1186 j = i - sh_prochk_minpid;
1187
1188 if (((res[j] & SH_PR_PS) != 0) || ((res[j] & SH_PR_PS2) != 0))
1189 {
1190 res[j] |= SH_PR_PS_ANY;
1191 }
1192 else
1193 {
1194 res[j] &= ~SH_PR_PS_ANY;
1195 }
1196
1197 tests[0] = '\0';
1198
1199 if ((res[j] & SH_PR_ANY) || (res[j] & SH_PR_PS_ANY))
1200 {
1201 /* list all tests where the pid was found
1202 */
1203 sh_processes_tlist (tests, sizeof(tests), res[j]);
1204
1205 /*
1206 * case 1: in ps and found
1207 */
1208 if ((res[j] & SH_PR_PS_ANY) && (res[j] & SH_PR_ANY))
1209 {
1210 SH_MUTEX_LOCK(mutex_thread_nolog);
1211 sh_error_handle((-1), FIL__, __LINE__, 0, MSG_PCK_OK,
1212 (unsigned long) i, tests);
1213 SH_MUTEX_UNLOCK(mutex_thread_nolog);
1214 }
1215
1216 /*
1217 * case 2: not in ps and found
1218 */
1219 else if ((res[j] & SH_PR_PS_ANY) == 0)
1220 {
1221 res[j] = sh_processes_check ((pid_t) i, 0);
1222 /*
1223 * if still there, it is real and hidden
1224 */
1225 if ((res[j] & SH_PR_ANY) && !openvz_ok(res, j))
1226 {
1227 if (S_FALSE == is_in_list(&list_hidden, NULL, i))
1228 {
1229 char user[16];
1230 char * aout;
1231 char * safe;
1232
1233 SH_MUTEX_LOCK(mutex_thread_nolog);
1234 aout = get_user_and_path ((pid_t) i, user, sizeof(user));
1235 SH_MUTEX_UNLOCK(mutex_thread_nolog);
1236
1237 if (aout)
1238 {
1239 safe = sh_util_safe_name (aout);
1240 SH_MUTEX_LOCK(mutex_thread_nolog);
1241 sh_error_handle(sh_prochk_severity, FIL__, __LINE__, 0,
1242 MSG_PCK_P_HIDDEN,
1243 (unsigned long) i, tests, safe, user);
1244 SH_MUTEX_UNLOCK(mutex_thread_nolog);
1245 SH_FREE(safe);
1246 SH_FREE(aout);
1247 }
1248 else
1249 {
1250 SH_MUTEX_LOCK(mutex_thread_nolog);
1251 sh_error_handle(sh_prochk_severity, FIL__, __LINE__, 0,
1252 MSG_PCK_HIDDEN,
1253 (unsigned long) i, tests);
1254 SH_MUTEX_UNLOCK(mutex_thread_nolog);
1255 }
1256 }
1257 }
1258 }
1259
1260 /*
1261 * case 3: in ps, but not found
1262 */
1263 else
1264 {
1265 if (((res[j] & SH_PR_PS) != 0) && ((res[j] & SH_PR_PS2) != 0))
1266 {
1267 if (S_FALSE == is_in_list(&list_fake, NULL, i))
1268 {
1269 SH_MUTEX_LOCK(mutex_thread_nolog);
1270 sh_error_handle(sh_prochk_severity, FIL__, __LINE__, 0,
1271 MSG_PCK_FAKE,
1272 (unsigned long) i, tests);
1273 SH_MUTEX_UNLOCK(mutex_thread_nolog);
1274 }
1275 }
1276 }
1277 }
1278 } /* loop end */
1279
1280 check_watchlist (res);
1281
1282 SL_RETURN (0, _("sh_process_check_int"));
1283}
1284
1285/* Initialise.
1286 */
1287static int sh_prochk_init_internal(void)
1288{
1289 SL_ENTER(_("sh_prochk_init"));
1290
1291 (void) proc_max_pid (&sh_prochk_maxpid);
1292
1293 if (sh_prochk_minpid > sh_prochk_maxpid)
1294 ShProchkActive = S_FALSE;
1295
1296 /* We need to free anything allocated by the configuration functions if
1297 * we find that the module is to be left inactive - otherwise _reconf()
1298 * won't quite work.
1299 */
1300 if( ShProchkActive == S_FALSE )
1301 {
1302 sh_prochk_free_list(process_check);
1303 process_check = NULL;
1304 SL_RETURN(-1, _("sh_prochk_init"));
1305 }
1306
1307 sh_prochk_size = sh_prochk_maxpid - sh_prochk_minpid;
1308
1309 sh_prochk_res = SH_ALLOC(sizeof(short) * sh_prochk_size);
1310 memset (sh_prochk_res, 0, sizeof(short) * sh_prochk_size);
1311
1312 SL_RETURN(0, _("sh_prochk_init"));
1313}
1314
1315int sh_prochk_init (struct mod_type * arg)
1316{
1317#ifndef HAVE_PTHREAD
1318 (void) arg;
1319#endif
1320
1321 if (ShProchkActive == S_FALSE)
1322 return SH_MOD_FAILED;
1323#ifdef HAVE_PTHREAD
1324 if (arg != NULL && arg->initval < 0 &&
1325 (sh.flag.isdaemon == S_TRUE || sh.flag.loop == S_TRUE))
1326 {
1327 if (0 == sh_pthread_create(sh_threaded_module_run, (void *)arg))
1328 return SH_MOD_THREAD;
1329 else
1330 return SH_MOD_FAILED;
1331 }
1332#endif
1333 return sh_prochk_init_internal();
1334}
1335
1336int sh_prochk_timer(time_t tcurrent)
1337{
1338 static time_t lastcheck = 0;
1339
1340 SL_ENTER(_("sh_prochk_timer"));
1341 if ((time_t) (tcurrent - lastcheck) >= sh_prochk_interval)
1342 {
1343 lastcheck = tcurrent;
1344 SL_RETURN((-1), _("sh_prochk_timer"));
1345 }
1346 SL_RETURN(0, _("sh_prochk_timer"));
1347}
1348
1349int sh_prochk_check(void)
1350{
1351 int status;
1352
1353 SL_ENTER(_("sh_prochk_check"));
1354
1355 SH_MUTEX_LOCK(mutex_proc_check);
1356
1357 status = 0;
1358
1359 if( ShProchkActive != S_FALSE )
1360 {
1361 SH_MUTEX_LOCK(mutex_thread_nolog);
1362 sh_error_handle((-1), FIL__, __LINE__, 0, MSG_PCK_CHECK,
1363 (unsigned long) sh_prochk_minpid,
1364 (unsigned long) (sh_prochk_maxpid-1));
1365 SH_MUTEX_UNLOCK(mutex_thread_nolog);
1366
1367 if (sh_prochk_res) {
1368 memset (sh_prochk_res, 0, sizeof(short) * sh_prochk_size);
1369 }
1370 status = sh_process_check_int(sh_prochk_res);
1371
1372 if (status != 0)
1373 ShProchkActive = S_FALSE;
1374
1375 /* clean out old entries which are not marked
1376 * as missing/hidden/fake anymore
1377 */
1378 clean_list (&list_missing);
1379 clean_list (&list_hidden);
1380 clean_list (&list_fake);
1381 }
1382
1383 SH_MUTEX_UNLOCK(mutex_proc_check);
1384
1385 SL_RETURN(status, _("sh_prochk_check"));
1386}
1387
1388/* Free our lists and the associated memory
1389 */
1390int sh_prochk_cleanup(void)
1391{
1392 SL_ENTER(_("sh_prochk_cleanup"));
1393
1394 sh_prochk_reconf();
1395
1396 if (list_missing) {
1397 kill_list(list_missing);
1398 list_missing = NULL;
1399 }
1400 if (list_hidden) {
1401 kill_list(list_hidden);
1402 list_hidden = NULL;
1403 }
1404 if (list_fake) {
1405 kill_list(list_fake);
1406 list_fake = NULL;
1407 }
1408
1409 SL_RETURN(0, _("sh_prochk_cleanup"));
1410}
1411
1412/* Free our lists and the associated memory
1413 */
1414int sh_prochk_reconf(void)
1415{
1416 SL_ENTER(_("sh_prochk_reconf"));
1417
1418 SH_MUTEX_LOCK(mutex_proc_check);
1419 userdef_maxpid = 0;
1420 sh_prochk_maxpid = 0x8000;
1421 sh_prochk_minpid = 0x0001;
1422 sh_prochk_interval = SH_PROCHK_INTERVAL;
1423 sh_prochk_openvz = S_FALSE;
1424 p_store = 0;
1425 openvz_hidden = 0;
1426
1427 sh_prochk_free_list(process_check);
1428 process_check = NULL;
1429 if (sh_prochk_res != NULL)
1430 SH_FREE(sh_prochk_res);
1431 sh_prochk_res = NULL;
1432
1433 if (sh_prochk_psarg)
1434 SH_FREE(sh_prochk_psarg);
1435 sh_prochk_psarg = NULL;
1436 if (sh_prochk_pspath)
1437 SH_FREE(sh_prochk_pspath);
1438 sh_prochk_pspath = NULL;
1439 SH_MUTEX_UNLOCK(mutex_proc_check);
1440
1441 SL_RETURN(0, _("sh_prochk_reconf"));
1442}
1443
1444/* #if defined (SH_WITH_CLIENT) || defined (SH_STANDALONE) */
1445#endif
1446
1447/* #ifdef SH_USE_PROCESSCHECK */
1448#endif
1449
1450
1451#ifdef SH_CUTEST
1452#include "CuTest.h"
1453
1454void Test_processcheck_watchlist_ok (CuTest *tc) {
1455#if defined(SH_USE_PROCESSCHECK) && (defined(SH_WITH_CLIENT) || defined(SH_STANDALONE))
1456 CuAssertTrue(tc, 0 == sh_prochk_add_process("init"));
1457 CuAssertTrue(tc,
1458 S_TRUE == is_in_watchlist(" 1 ? 00:00:00 init", 0));
1459 CuAssertTrue(tc,
1460 S_FALSE == is_in_watchlist(" 1 ? 00:00:00 flix", 0));
1461 CuAssertTrue(tc,
1462 S_TRUE == is_in_watchlist("25218 ? SNs 0:01 /usr/sbin/init -k start -DSSL", 0));
1463 CuAssertTrue(tc,
1464 S_FALSE == is_in_watchlist("25218 ? SNs 0:01 /usr/sbin/apache2 -k start -DSSL", 0));
1465
1466
1467 sh_prochk_free_list(process_check);
1468 process_check = NULL;
1469 CuAssertTrue(tc, S_FALSE == is_in_watchlist("init", 0));
1470
1471 CuAssertTrue(tc, 0 == sh_prochk_add_process("init"));
1472 CuAssertTrue(tc, 0 == sh_prochk_add_process("ssh"));
1473 CuAssertTrue(tc, 0 == sh_prochk_add_process("syslog"));
1474 CuAssertTrue(tc, S_TRUE == is_in_watchlist("init", 0));
1475 CuAssertTrue(tc, S_TRUE == is_in_watchlist("ssh", 0));
1476 CuAssertTrue(tc, S_TRUE == is_in_watchlist("syslog", 0));
1477
1478 sh_prochk_free_list(process_check);
1479 process_check = NULL;
1480 CuAssertTrue(tc, S_FALSE == is_in_watchlist("init", 0));
1481 CuAssertTrue(tc, S_FALSE == is_in_watchlist("ssh", 0));
1482 CuAssertTrue(tc, S_FALSE == is_in_watchlist("syslog", 0));
1483#else
1484 (void) tc; /* fix compiler warning */
1485#endif
1486 return;
1487}
1488
1489void Test_processcheck_listhandle_ok (CuTest *tc) {
1490#if defined(SH_USE_PROCESSCHECK) && (defined(SH_WITH_CLIENT) || defined(SH_STANDALONE))
1491 CuAssertTrue(tc, S_FALSE == is_in_list(&list_missing, "init", 0));
1492 CuAssertTrue(tc, S_TRUE == is_in_list(&list_missing, "init", 0));
1493 CuAssertTrue(tc, S_FALSE == is_in_list(&list_missing, "foobar", 0));
1494 CuAssertTrue(tc, S_TRUE == is_in_list(&list_missing, "foobar", 0));
1495
1496 if (list_missing)
1497 kill_list(list_missing);
1498 list_missing = NULL;
1499
1500 CuAssertTrue(tc, S_FALSE == is_in_list(&list_missing, "init", 0));
1501 CuAssertTrue(tc, S_TRUE == is_in_list(&list_missing, "init", 0));
1502 CuAssertTrue(tc, S_FALSE == is_in_list(&list_missing, "foobar", 0));
1503 CuAssertTrue(tc, S_TRUE == is_in_list(&list_missing, "foobar", 0));
1504
1505 if (list_missing)
1506 kill_list(list_missing);
1507 list_missing = NULL;
1508
1509 CuAssertTrue(tc, S_FALSE == is_in_list(&list_missing, "init", 0));
1510 CuAssertTrue(tc, S_TRUE == is_in_list(&list_missing, "init", 0));
1511 CuAssertTrue(tc, S_FALSE == is_in_list(&list_missing, "foobar", 0));
1512 CuAssertTrue(tc, S_TRUE == is_in_list(&list_missing, "foobar", 0));
1513
1514 CuAssertTrue(tc, 2 == clean_list(&list_missing));
1515 CuAssertPtrNotNull(tc, list_missing);
1516
1517 CuAssertTrue(tc, S_TRUE == is_in_list(&list_missing, "init", 0));
1518 CuAssertTrue(tc, S_TRUE == is_in_list(&list_missing, "foobar", 0));
1519
1520 CuAssertTrue(tc, 2 == clean_list(&list_missing));
1521 CuAssertPtrNotNull(tc, list_missing);
1522
1523 CuAssertTrue(tc, 0 == clean_list(&list_missing));
1524 CuAssertTrue(tc, NULL == list_missing);
1525#else
1526 (void) tc; /* fix compiler warning */
1527#endif
1528 return;
1529}
1530
1531
1532/* #ifdef SH_CUTEST */
1533#endif
1534
Note: See TracBrowser for help on using the repository browser.