logread: eliminate usage of data/bss
[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 enum { KEY_ID = 0x414e4547 }; /* "GENA" */
20
21 struct shbuf_ds {
22         int32_t size;           // size of data - 1
23         int32_t tail;           // end of message list
24         char data[1];           // messages
25 };
26
27 static const struct sembuf init_sem[3] = {
28         {0, -1, IPC_NOWAIT | SEM_UNDO},
29         {1, 0}, {0, +1, SEM_UNDO}
30 };
31
32 struct globals {
33         struct sembuf SMrup[1]; // {0, -1, IPC_NOWAIT | SEM_UNDO},
34         struct sembuf SMrdn[2]; // {1, 0}, {0, +1, SEM_UNDO}
35         struct shbuf_ds *shbuf;
36 };
37 #define G (*(struct globals*)&bb_common_bufsiz1)
38 #define SMrup (G.SMrup)
39 #define SMrdn (G.SMrdn)
40 #define shbuf (G.shbuf)
41 #define INIT_G() \
42         do { \
43                 memcpy(SMrup, init_sem, sizeof(init_sem)); \
44         } while (0)
45
46 static void error_exit(const char *str) ATTRIBUTE_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 ATTRIBUTE_UNUSED)
64 {
65         signal(SIGINT, SIG_IGN);
66         shmdt(shbuf);
67         exit(0);
68 }
69
70 int logread_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
71 int logread_main(int argc, char **argv)
72 {
73         int cur;
74         int log_semid; /* ipc semaphore id */
75         int log_shmid; /* ipc shared memory id */
76         smallint follow = getopt32(argv, "f");
77
78         log_shmid = shmget(KEY_ID, 0, 0);
79         if (log_shmid == -1)
80                 bb_perror_msg_and_die("can't find syslogd buffer");
81
82         /* Attach shared memory to our char* */
83         shbuf = shmat(log_shmid, NULL, SHM_RDONLY);
84         if (shbuf == NULL)
85                 bb_perror_msg_and_die("can't access syslogd buffer");
86
87         log_semid = semget(KEY_ID, 0, 0);
88         if (log_semid == -1)
89                 error_exit("can't get access to semaphores for syslogd buffer");
90
91         signal(SIGINT, interrupted);
92
93         /* Suppose atomic memory read */
94         /* Max possible value for tail is shbuf->size - 1 */
95         cur = shbuf->tail;
96
97         /* Loop for logread -f, one pass if there was no -f */
98         do {
99                 unsigned shbuf_size;
100                 unsigned shbuf_tail;
101                 const char *shbuf_data;
102 #if ENABLE_FEATURE_LOGREAD_REDUCED_LOCKING
103                 int i;
104                 int len_first_part;
105                 int len_total = len_total; /* for gcc */
106                 char *copy = copy; /* for gcc */
107 #endif
108                 if (semop(log_semid, SMrdn, 2) == -1)
109                         error_exit("semop[SMrdn]");
110
111                 /* Copy the info, helps gcc to realize that it doesn't change */
112                 shbuf_size = shbuf->size;
113                 shbuf_tail = shbuf->tail;
114                 shbuf_data = shbuf->data; /* pointer! */
115
116                 if (DEBUG)
117                         printf("cur:%d tail:%i size:%i\n",
118                                         cur, shbuf_tail, shbuf_size);
119
120                 if (!follow) {
121                         /* advance to oldest complete message */
122                         /* find NUL */
123                         cur += strlen(shbuf_data + cur);
124                         if (cur >= shbuf_size) { /* last byte in buffer? */
125                                 cur = strnlen(shbuf_data, shbuf_tail);
126                                 if (cur == shbuf_tail)
127                                         goto unlock; /* no complete messages */
128                         }
129                         /* advance to first byte of the message */
130                         cur++;
131                         if (cur >= shbuf_size) /* last byte in buffer? */
132                                 cur = 0;
133                 } else { /* logread -f */
134                         if (cur == shbuf_tail) {
135                                 sem_up(log_semid);
136                                 fflush(stdout);
137                                 sleep(1); /* TODO: replace me with a sleep_on */
138                                 continue;
139                         }
140                 }
141
142                 /* Read from cur to tail */
143 #if ENABLE_FEATURE_LOGREAD_REDUCED_LOCKING
144                 len_first_part = len_total = shbuf_tail - cur;
145                 if (len_total < 0) {
146                         /* message wraps: */
147                         /* [SECOND PART.........FIRST PART] */
148                         /*  ^data      ^tail    ^cur      ^size */
149                         len_total += shbuf_size;
150                 }
151                 copy = xmalloc(len_total + 1);
152                 if (len_first_part < 0) {
153                         /* message wraps (see above) */
154                         len_first_part = shbuf_size - cur;
155                         memcpy(copy + len_first_part, shbuf_data, shbuf_tail);
156                 }
157                 memcpy(copy, shbuf_data + cur, len_first_part);
158                 copy[len_total] = '\0';
159                 cur = shbuf_tail;
160 #else
161                 while (cur != shbuf_tail) {
162                         fputs(shbuf_data + cur, stdout);
163                         cur += strlen(shbuf_data + cur) + 1;
164                         if (cur >= shbuf_size)
165                                 cur = 0;
166                 }
167 #endif
168  unlock:
169                 /* release the lock on the log chain */
170                 sem_up(log_semid);
171
172 #if ENABLE_FEATURE_LOGREAD_REDUCED_LOCKING
173                 for (i = 0; i < len_total; i += strlen(copy + i) + 1) {
174                         fputs(copy + i, stdout);
175                 }
176                 free(copy);
177 #endif
178         } while (follow);
179
180         shmdt(shbuf);
181
182         fflush_stdout_and_exit(EXIT_SUCCESS);
183 }