fix #>&- syntax for closing fds
[oweals/busybox.git] / sysklogd / logread.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * circular buffer syslog implementation for busybox
4  *
5  * Copyright (C) 2000 by Gennady Feldman <gfeldman@gena01.com>
6  *
7  * Maintainer: Gennady Feldman <gfeldman@gena01.com> as of Mar 12, 2001
8  *
9  * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
10  */
11
12 #include "libbb.h"
13 #include <sys/ipc.h>
14 #include <sys/sem.h>
15 #include <sys/shm.h>
16
17 #define DEBUG 0
18
19 /* our shared key (syslogd.c and logread.c must be in sync) */
20 enum { KEY_ID = 0x414e4547 }; /* "GENA" */
21
22 struct shbuf_ds {
23         int32_t size;           // size of data - 1
24         int32_t tail;           // end of message list
25         char data[1];           // messages
26 };
27
28 static const struct sembuf init_sem[3] = {
29         {0, -1, IPC_NOWAIT | SEM_UNDO},
30         {1, 0}, {0, +1, SEM_UNDO}
31 };
32
33 struct globals {
34         struct sembuf SMrup[1]; // {0, -1, IPC_NOWAIT | SEM_UNDO},
35         struct sembuf SMrdn[2]; // {1, 0}, {0, +1, SEM_UNDO}
36         struct shbuf_ds *shbuf;
37 };
38 #define G (*(struct globals*)&bb_common_bufsiz1)
39 #define SMrup (G.SMrup)
40 #define SMrdn (G.SMrdn)
41 #define shbuf (G.shbuf)
42 #define INIT_G() do { \
43         memcpy(SMrup, init_sem, sizeof(init_sem)); \
44 } while (0)
45
46 static void error_exit(const char *str) NORETURN;
47 static void error_exit(const char *str)
48 {
49         //release all acquired resources
50         shmdt(shbuf);
51         bb_perror_msg_and_die(str);
52 }
53
54 /*
55  * sem_up - up()'s a semaphore.
56  */
57 static void sem_up(int semid)
58 {
59         if (semop(semid, SMrup, 1) == -1)
60                 error_exit("semop[SMrup]");
61 }
62
63 static void interrupted(int sig UNUSED_PARAM)
64 {
65         signal(SIGINT, SIG_IGN);
66         shmdt(shbuf);
67         exit(EXIT_SUCCESS);
68 }
69
70 int logread_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
71 int logread_main(int argc UNUSED_PARAM, char **argv)
72 {
73         unsigned cur;
74         int log_semid; /* ipc semaphore id */
75         int log_shmid; /* ipc shared memory id */
76         smallint follow = getopt32(argv, "f");
77
78         INIT_G();
79
80         log_shmid = shmget(KEY_ID, 0, 0);
81         if (log_shmid == -1)
82                 bb_perror_msg_and_die("can't find syslogd buffer");
83
84         /* Attach shared memory to our char* */
85         shbuf = shmat(log_shmid, NULL, SHM_RDONLY);
86         if (shbuf == NULL)
87                 bb_perror_msg_and_die("can't access syslogd buffer");
88
89         log_semid = semget(KEY_ID, 0, 0);
90         if (log_semid == -1)
91                 error_exit("can't get access to semaphores for syslogd buffer");
92
93         signal(SIGINT, interrupted);
94
95         /* Suppose atomic memory read */
96         /* Max possible value for tail is shbuf->size - 1 */
97         cur = shbuf->tail;
98
99         /* Loop for logread -f, one pass if there was no -f */
100         do {
101                 unsigned shbuf_size;
102                 unsigned shbuf_tail;
103                 const char *shbuf_data;
104 #if ENABLE_FEATURE_LOGREAD_REDUCED_LOCKING
105                 int i;
106                 int len_first_part;
107                 int len_total = len_total; /* for gcc */
108                 char *copy = copy; /* for gcc */
109 #endif
110                 if (semop(log_semid, SMrdn, 2) == -1)
111                         error_exit("semop[SMrdn]");
112
113                 /* Copy the info, helps gcc to realize that it doesn't change */
114                 shbuf_size = shbuf->size;
115                 shbuf_tail = shbuf->tail;
116                 shbuf_data = shbuf->data; /* pointer! */
117
118                 if (DEBUG)
119                         printf("cur:%d tail:%i size:%i\n",
120                                         cur, shbuf_tail, shbuf_size);
121
122                 if (!follow) {
123                         /* advance to oldest complete message */
124                         /* find NUL */
125                         cur += strlen(shbuf_data + cur);
126                         if (cur >= shbuf_size) { /* last byte in buffer? */
127                                 cur = strnlen(shbuf_data, shbuf_tail);
128                                 if (cur == shbuf_tail)
129                                         goto unlock; /* no complete messages */
130                         }
131                         /* advance to first byte of the message */
132                         cur++;
133                         if (cur >= shbuf_size) /* last byte in buffer? */
134                                 cur = 0;
135                 } else { /* logread -f */
136                         if (cur == shbuf_tail) {
137                                 sem_up(log_semid);
138                                 fflush(stdout);
139                                 sleep(1); /* TODO: replace me with a sleep_on */
140                                 continue;
141                         }
142                 }
143
144                 /* Read from cur to tail */
145 #if ENABLE_FEATURE_LOGREAD_REDUCED_LOCKING
146                 len_first_part = len_total = shbuf_tail - cur;
147                 if (len_total < 0) {
148                         /* message wraps: */
149                         /* [SECOND PART.........FIRST PART] */
150                         /*  ^data      ^tail    ^cur      ^size */
151                         len_total += shbuf_size;
152                 }
153                 copy = xmalloc(len_total + 1);
154                 if (len_first_part < 0) {
155                         /* message wraps (see above) */
156                         len_first_part = shbuf_size - cur;
157                         memcpy(copy + len_first_part, shbuf_data, shbuf_tail);
158                 }
159                 memcpy(copy, shbuf_data + cur, len_first_part);
160                 copy[len_total] = '\0';
161                 cur = shbuf_tail;
162 #else
163                 while (cur != shbuf_tail) {
164                         fputs(shbuf_data + cur, stdout);
165                         cur += strlen(shbuf_data + cur) + 1;
166                         if (cur >= shbuf_size)
167                                 cur = 0;
168                 }
169 #endif
170  unlock:
171                 /* release the lock on the log chain */
172                 sem_up(log_semid);
173
174 #if ENABLE_FEATURE_LOGREAD_REDUCED_LOCKING
175                 for (i = 0; i < len_total; i += strlen(copy + i) + 1) {
176                         fputs(copy + i, stdout);
177                 }
178                 free(copy);
179 #endif
180         } while (follow);
181
182         shmdt(shbuf);
183
184         fflush_stdout_and_exit(EXIT_SUCCESS);
185 }