source: trunk/src/sh_sub.c@ 361

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

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

File size: 11.0 KB
RevLine 
[316]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
[321]22/* 0->1 for debug */
23#if 0
24#define SH_SUB_DBG 1
25#endif
26
[316]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
[321]69void sh_kill_sub()
[316]70{
71 SH_MUTEX_LOCK(mutex_sub);
[321]72
[316]73 if (sh_child_pid != -1)
74 {
75 int status;
[321]76#ifdef WCONTINUED
77 int wflags = WNOHANG|WUNTRACED|WCONTINUED;
78#else
79 int wflags = WNOHANG|WUNTRACED;
80#endif
[316]81
82 close (parent2child[1]);
83 close (child2parent[0]);
84
[321]85 /* fprintf(stderr, "FIXME kill_sub %d\n", (int) sh_child_pid); */
[316]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)
[321]93 sh_wait_ret = waitpid( -1, &status, wflags);
[316]94 else
[321]95 sh_wait_ret = waitpid(sh_child_pid, &status, wflags);
[316]96
97 sh_child_pid = -1;
98 }
[321]99
[316]100 SH_MUTEX_UNLOCK(mutex_sub);
101 return;
102}
103
104static int sh_create_sub()
105{
106 pid_t res;
[321]107 volatile int retval = 0;
[316]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
[321]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
[316]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:
[323]232 ; /* 'label at end of compound statement' */
[316]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
[321]297 /* fprintf(stderr, "FIXME wait_com polling..\n"); */
[316]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
[321]329 /* fprintf(stderr, "FIXME wait_com writing..\n"); */
[316]330
331 ret = sh_sub_write(child2parent[1], &outbuf, sizeof(outbuf));
332 if (ret < 0)
333 {
[321]334 /* fprintf(stderr, "FIXME wait_com return 1\n"); */
[316]335 return;
336 }
337 }
338 else /* sh_sub_read() < 0 */
339 {
[321]340 /* fprintf(stderr, "FIXME wait_com return 2\n"); */
[316]341 return;
342 }
343 }
344
[321]345 /* fprintf(stderr, "FIXME wait_com next..\n"); */
[316]346
347 } while (1 == 1);
348}
349
[325]350#ifndef ETIMEDOUT
351#define ETIMEDOUT EIO
352#endif
353
[316]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 {
[325]377 if (rcount >= 0)
378 errno = ETIMEDOUT;
[316]379 return -1;
380 }
381 }
[325]382 } while (count > 0 &&
383 (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR));
[316]384
385 if (count > 0)
386 return -1;
387
388 return 0;
389}
390
[321]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
[316]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
[321]446#ifdef SH_SUB_DBG
447 debug_it("%d sh_child_pid %d\n", (int)getpid(), (int) sh_child_pid);
448#endif
449
[316]450 if (sh_child_pid == -1)
451 sh_create_sub();
452
[321]453#ifdef SH_SUB_DBG
454 debug_it("%d stat_sub %s (%d)\n", (int)getpid(), inbuf.path, (int) sh_child_pid);
455#endif
[316]456
457 SH_MUTEX_LOCK(mutex_sub_work);
458
459 retval = sh_sub_write(parent2child[1], &inbuf, sizeof(inbuf));
460 if (retval < 0)
461 {
[324]462 int error = errno;
[316]463 sh_kill_sub();
[324]464 errno = error;
[316]465 sflag = 1;
466 goto end;
467 }
468
[321]469#ifdef SH_SUB_DBG
470 debug_it("%d stat_sub polling..\n", (int)getpid());
471#endif
[316]472
473 pfds.fd = child2parent[0];
474 pfds.events = POLLIN;
475
476 do {
[324]477 retval = poll(&pfds, 1, 300 * 1000);
[316]478 } while (retval < 0 && errno == EINTR);
479
480 if (retval <= 0)
481 {
[324]482 int error = errno;
[316]483 sh_kill_sub();
[324]484 errno = (retval == 0) ? ETIMEDOUT : error;
[316]485 sflag = -1;
486 goto end;
487 }
488
[321]489#ifdef SH_SUB_DBG
490 debug_it("%d stat_sub reading..\n", (int)getpid());
491#endif
[316]492
493 retval = sh_sub_read (child2parent[0], &outbuf, sizeof(outbuf));
494 if (retval < 0)
495 {
[324]496 int error = errno;
[316]497 sh_kill_sub();
[324]498 errno = error;
[316]499 sflag = 1;
500 goto end;
501 }
502
503 end:
[323]504 ; /* 'label at end of compound statement' */
[316]505 SH_MUTEX_UNLOCK(mutex_sub_work);
506
507 if (sflag == 0)
508 {
[321]509#ifdef SH_SUB_DBG
510 debug_it("%d stat_sub done..\n", (int)getpid());
511#endif
[316]512 memcpy(buf, &(outbuf.sbuf), sizeof(struct stat));
513 errno = outbuf.errnum;
514 return outbuf.retval;
515 }
516 else if (sflag == 1)
517 {
[321]518#ifdef SH_SUB_DBG
519 debug_it("%d stat_sub error..\n", (int)getpid());
520#endif
[316]521 /* could not read, thus subprocess may have gone */
[321]522 sflag = 0;
[316]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.