source: trunk/src/sh_sub.c @ 325

Last change on this file since 325 was 325, checked in by katerina, 11 years ago

More fixes for ticket #244: timeout error in retry_lstat() not propagated

File size: 11.0 KB
Line 
1/* SAMHAIN file system integrity testing                                   */
2/* Copyright (C) 2011 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#include "config_xor.h"
21
22/* 0->1 for debug */ 
23#if 0
24#define SH_SUB_DBG 1
25#endif
26
27#ifndef NULL
28#if !defined(__cplusplus)
29#define NULL ((void*)0)
30#else
31#define NULL (0)
32#endif
33#endif
34
35
36#include <stdio.h>
37#include <string.h>
38#include <errno.h>
39#include <limits.h>
40
41#include <sys/types.h>
42#include <sys/stat.h>
43#include <sys/wait.h>
44#include <unistd.h>
45#include <fcntl.h>
46#include <poll.h>
47
48#include "samhain.h"
49#include "sh_pthread.h"
50
51#ifndef HAVE_LSTAT
52#define lstat stat
53#endif
54
55#define FIL__ _("sh_sub.c")
56
57static pid_t sh_child_pid = -1;
58static pid_t sh_wait_ret  =  1;
59
60static int parent2child[2];
61static int child2parent[2];
62
63SH_MUTEX_STATIC(mutex_sub,      PTHREAD_MUTEX_INITIALIZER);
64SH_MUTEX_STATIC(mutex_sub_work, PTHREAD_MUTEX_INITIALIZER);
65
66static void wait_for_command();
67static ssize_t sh_sub_read(int fd, void *buf, size_t count);
68
69void sh_kill_sub()
70{
71  SH_MUTEX_LOCK(mutex_sub);
72
73  if (sh_child_pid != -1)
74    {
75      int status;
76#ifdef WCONTINUED
77      int wflags = WNOHANG|WUNTRACED|WCONTINUED;
78#else
79      int wflags = WNOHANG|WUNTRACED;
80#endif
81
82      close (parent2child[1]);
83      close (child2parent[0]);
84
85      /* fprintf(stderr, "FIXME kill_sub %d\n", (int) sh_child_pid); */
86
87      /* Let's be rude. */
88      kill(sh_child_pid, SIGKILL);
89
90      retry_msleep(1,0);
91
92      if (sh_wait_ret == 0)
93        sh_wait_ret = waitpid(          -1, &status, wflags);
94      else
95        sh_wait_ret = waitpid(sh_child_pid, &status, wflags);
96
97      sh_child_pid = -1;
98    }
99
100  SH_MUTEX_UNLOCK(mutex_sub);
101  return;
102}
103
104static int sh_create_sub()
105{
106  pid_t res;
107  volatile int   retval = 0;
108
109  SH_MUTEX_LOCK(mutex_sub);
110
111#if !defined(O_NONBLOCK)
112#if defined(O_NDELAY)
113#define O_NONBLOCK  O_NDELAY
114#else
115#define O_NONBLOCK  0
116#endif
117#endif
118
119  if (sh_child_pid == -1)
120    {
121      sigset_t signal_set_new;
122      sigset_t signal_set_old;
123
124      sigfillset ( &signal_set_new );
125      sigemptyset( &signal_set_old );
126
127      /* Create pipes. */
128      res = pipe (parent2child);
129      if (res == 0)
130        res = pipe (child2parent);
131     
132      if (res != 0)
133        goto out;
134
135      SH_SETSIGMASK(SIG_BLOCK, &signal_set_new, &signal_set_old);
136
137      res = fork();
138     
139      if (res == 0)
140        {
141          /* Child process. */
142#ifdef _SC_OPEN_MAX
143          int fdlimit = sysconf (_SC_OPEN_MAX);
144#else
145#ifdef OPEN_MAX
146          int fdlimit = OPEN_MAX;
147#else
148          int fdlimit = _POSIX_OPEN_MAX;
149#endif
150#endif
151          int sflags, i, fd = 0;
152          struct sigaction act;
153
154          /* zero private information
155           */
156          memset(skey, 0, sizeof(sh_key_t)); 
157
158          close (parent2child[1]);
159          close (child2parent[0]);
160         
161          sflags = fcntl(parent2child[0], F_GETFL, 0);
162          fcntl(parent2child[0], F_SETFL, sflags | O_NONBLOCK);
163          sflags = fcntl(child2parent[1], F_GETFL, 0);
164          fcntl(child2parent[1], F_SETFL, sflags | O_NONBLOCK);
165
166          /* close inherited file descriptors
167           */
168          if (fdlimit < 0) 
169            fdlimit = 20;  /* POSIX lower limit */
170          while (fd < fdlimit)
171            {
172              if (fd != parent2child[0] && fd != child2parent[1])
173                close(fd);
174              ++fd;
175            }
176
177          /*
178          for (i = 0; i < 3; ++i)
179            {
180              if ( fcntl(i, F_GETFL, 0) == (-1))
181                (void) open(_("/dev/null"), O_RDWR, 0);
182            }
183          */
184
185          /* reset signal handling
186           */
187          act.sa_handler = SIG_DFL;
188          for (i = 0; i < NSIG; ++i)
189            sigaction(i, &act, NULL);
190          SH_SETSIGMASK(SIG_UNBLOCK, &signal_set_new, NULL);
191
192          wait_for_command();
193         
194          _exit(0);
195        }
196      else if (res > 0)
197        {
198          /* Parent process. */
199          int sflags;
200
201          SH_SETSIGMASK(SIG_SETMASK, &signal_set_old, NULL);
202
203          close (parent2child[0]);
204          close (child2parent[1]);
205         
206          sflags = fcntl(parent2child[1], F_GETFL, 0);
207          fcntl(parent2child[1], F_SETFL, sflags | O_NONBLOCK);
208          sflags = fcntl(child2parent[0], F_GETFL, 0);
209          fcntl(child2parent[0], F_SETFL, sflags | O_NONBLOCK);
210
211          sh_child_pid = res;
212
213          /* fprintf(stderr, "FIXME create_sub %d\n", (int) sh_child_pid); */
214        }
215      else
216        {
217          /* Failure. */
218
219          SH_SETSIGMASK(SIG_SETMASK, &signal_set_old, NULL);
220
221          close (parent2child[0]);
222          close (parent2child[1]);
223
224          close (child2parent[0]);
225          close (child2parent[1]);
226         
227          retval = -1;
228        }
229    }
230
231 out:
232  ; /* 'label at end of compound statement' */
233  SH_MUTEX_UNLOCK(mutex_sub);
234  return retval;
235}
236
237#define  SH_SUB_BUF (PIPE_BUF-1)
238struct sh_sub_in {
239  char   command;
240  char   path[SH_SUB_BUF];
241};
242
243struct sh_sub_out {
244  int retval;
245  int errnum;
246  struct stat sbuf;
247};
248
249#define SH_COM_STAT  0
250#define SH_COM_LSTAT 1
251
252static ssize_t sh_sub_write(int fd, const void *buf, size_t count)
253{
254  ssize_t rcount;
255  int ttl = 5; /* 0, 1, 9, 81, 729 millisec */
256  int tti = 1; 
257
258  do {
259
260    rcount = write(fd, buf, count);
261    if (rcount > 0) 
262      {
263        count -= rcount; buf += rcount; --ttl;
264      }
265
266    if (count > 0)
267      {
268        if (ttl > 0)
269          {
270            retry_msleep(0, tti);
271            tti *= 9;
272          }
273        else
274          {
275            return -1;
276          }
277      }
278  } while (count > 0 && (errno == EAGAIN || errno == EWOULDBLOCK));
279
280  if (count > 0)
281    return -1;
282  return 0;
283}
284
285static void wait_for_command()
286{
287  int               ret;
288  struct pollfd     fds;
289  struct sh_sub_in  inbuf;
290  struct sh_sub_out outbuf;
291
292  fds.fd     = parent2child[0];
293  fds.events = POLLIN;
294
295  do {
296
297    /* fprintf(stderr, "FIXME wait_com polling..\n"); */
298
299    do {
300      ret = poll(&fds, 1, -1);
301    } while (ret < 0 && errno == EINTR);
302
303    if (ret > 0)
304      {
305        ret = sh_sub_read(parent2child[0], &inbuf, sizeof(inbuf));
306
307        /*
308        fprintf(stderr, "FIXME wait_com stat %s (%s)\n",
309                inbuf.path, (inbuf.command == SH_COM_LSTAT) ? "lstat" : "stat");
310        */
311
312        if (ret == 0)
313          {
314            if (inbuf.command == SH_COM_LSTAT)
315              {
316                do { 
317                  outbuf.retval = lstat(inbuf.path, &(outbuf.sbuf)); 
318                } while (outbuf.retval < 0 && errno == EAGAIN);
319              }
320            else
321              {
322                do { 
323                  outbuf.retval = stat(inbuf.path, &(outbuf.sbuf)); 
324                } while (outbuf.retval < 0 && errno == EAGAIN);
325              }
326
327            outbuf.errnum = errno;
328
329            /* fprintf(stderr, "FIXME wait_com writing..\n"); */
330
331            ret = sh_sub_write(child2parent[1], &outbuf, sizeof(outbuf));
332            if (ret < 0)
333              {
334                /* fprintf(stderr, "FIXME wait_com return 1\n"); */
335                return;
336              }
337          }
338        else /* sh_sub_read() < 0 */
339          {
340            /* fprintf(stderr, "FIXME wait_com return 2\n"); */
341            return;
342          }
343      }
344   
345    /* fprintf(stderr, "FIXME wait_com next..\n"); */
346
347  } while (1 == 1);
348}
349
350#ifndef ETIMEDOUT
351#define ETIMEDOUT EIO
352#endif
353
354static ssize_t sh_sub_read(int fd, void *buf, size_t count)
355{
356  ssize_t rcount;
357  int ttl = 5; /* 0, 1, 9, 81, 729 millisec */
358  int tti = 1; 
359
360  do {
361    rcount = read(fd, buf, count);
362
363    if (rcount > 0) 
364      {
365        count -= rcount; buf += rcount; --ttl;
366      }
367
368    if (count > 0)
369      {
370        if (ttl > 0)
371          {
372            retry_msleep(0, tti);
373            tti *= 9;
374          }
375        else
376          {
377            if (rcount >= 0) 
378              errno = ETIMEDOUT;
379            return -1;
380          }
381      }
382  } while (count > 0 && 
383           (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR));
384
385  if (count > 0)
386    return -1;
387
388  return 0;
389}
390
391#ifdef SH_SUB_DBG
392#include <stdarg.h>
393static void debug_it (const char *fmt, ...)
394{
395  char msg[256];
396  va_list ap;
397
398  int fd = open("debug.it", O_CREAT|O_WRONLY|O_APPEND, 0666);
399
400  va_start(ap, fmt);
401  vsnprintf(msg, sizeof(msg), fmt, ap);  /* flawfinder: ignore */
402  va_end(ap);
403
404  write(fd, msg, strlen(msg));
405  write(fd, "\n", 1);
406  close(fd);
407  return;
408}
409#endif
410
411static int sh_sub_stat_int(const char *path, struct stat *buf, char command)
412{
413  int retval;
414  volatile int sflag = 0;
415  struct sh_sub_in  inbuf;
416  struct sh_sub_out outbuf;
417  struct pollfd     pfds;
418
419  size_t len = strlen(path) + 1;
420
421  if (len > SH_SUB_BUF)
422    {
423      if (command == SH_COM_LSTAT)
424        {
425          do { 
426            retval = lstat(path, buf); 
427          } while (retval < 0 && errno == EAGAIN);
428
429          return retval;
430        }
431      else
432        {
433          do { 
434            retval = stat(path, buf); 
435          } while (retval < 0 && errno == EAGAIN);
436
437          return retval;
438        }
439    }
440
441  sl_strlcpy(inbuf.path, path, SH_SUB_BUF);
442  inbuf.command = command;
443
444 start:
445
446#ifdef SH_SUB_DBG
447  debug_it("%d sh_child_pid %d\n", (int)getpid(), (int) sh_child_pid);
448#endif
449
450  if (sh_child_pid == -1)
451    sh_create_sub();
452
453#ifdef SH_SUB_DBG
454  debug_it("%d stat_sub %s (%d)\n", (int)getpid(), inbuf.path, (int) sh_child_pid);
455#endif
456
457  SH_MUTEX_LOCK(mutex_sub_work);
458
459  retval = sh_sub_write(parent2child[1], &inbuf, sizeof(inbuf));
460  if (retval < 0)
461    {
462      int error = errno;
463      sh_kill_sub();
464      errno = error;
465      sflag = 1;
466      goto end;
467    }
468
469#ifdef SH_SUB_DBG
470  debug_it("%d stat_sub polling..\n", (int)getpid());
471#endif
472
473  pfds.fd     = child2parent[0];
474  pfds.events = POLLIN;
475
476  do {
477    retval = poll(&pfds, 1, 300 * 1000);
478  } while (retval < 0 && errno == EINTR);
479
480  if (retval <= 0)
481    {
482      int error = errno;
483      sh_kill_sub();
484      errno = (retval == 0) ? ETIMEDOUT : error;
485      sflag = -1;
486      goto end;
487    }
488
489#ifdef SH_SUB_DBG
490  debug_it("%d stat_sub reading..\n", (int)getpid());
491#endif
492
493  retval = sh_sub_read (child2parent[0], &outbuf, sizeof(outbuf));
494  if (retval < 0)
495    {
496      int error = errno;
497      sh_kill_sub();
498      errno = error;
499      sflag = 1;
500      goto end;
501    }
502
503 end:
504  ; /* 'label at end of compound statement' */
505  SH_MUTEX_UNLOCK(mutex_sub_work);
506
507  if      (sflag == 0)
508    {
509#ifdef SH_SUB_DBG
510      debug_it("%d stat_sub done..\n", (int)getpid());
511#endif
512      memcpy(buf, &(outbuf.sbuf), sizeof(struct stat));
513      errno = outbuf.errnum;
514      return outbuf.retval;
515    }
516  else if (sflag == 1)
517    {
518#ifdef SH_SUB_DBG
519      debug_it("%d stat_sub error..\n", (int)getpid());
520#endif
521      /* could not read, thus subprocess may have gone */
522      sflag = 0;
523      goto start;
524    }
525
526  return -1;
527}
528
529int sh_sub_stat (const char *path, struct stat *buf)
530{
531  return sh_sub_stat_int(path, buf, SH_COM_STAT);
532}
533
534int sh_sub_lstat(const char *path, struct stat *buf)
535{
536  return sh_sub_stat_int(path, buf, SH_COM_LSTAT);
537}
538
Note: See TracBrowser for help on using the repository browser.