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