Skip to content

Commit c1eb969

Browse files
committed
Create threadsafe audit_fgets_r functions
Added a reentrant state structure and initialization logic for audit_fgets in audit-fgets.c Implemented thread-safe versions (audit_fgets_*_r) and wrappers that use a global state for compatibility Declared the new opaque type and function prototypes in libaudit.h
1 parent 3ea61d3 commit c1eb969

2 files changed

Lines changed: 102 additions & 30 deletions

File tree

lib/audit-fgets.c

Lines changed: 89 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include <string.h>
2626
#include <unistd.h>
2727
#include <errno.h>
28+
#include <stdlib.h>
2829
#include "libaudit.h"
2930

3031
/*
@@ -41,39 +42,66 @@
4142
*/
4243

4344
#define BUF_SIZE 8192
44-
static char buffer[2*BUF_SIZE+1] = { 0 };
45-
static char *current = buffer;
46-
static char *const eptr = buffer+(2*BUF_SIZE);
47-
static int eof = 0;
4845

49-
int audit_fgets_eof(void)
46+
struct audit_fgets_state {
47+
char buffer[2*BUF_SIZE+1];
48+
char *current;
49+
char *eptr;
50+
int eof;
51+
};
52+
53+
static struct audit_fgets_state global_state;
54+
static int global_init_done;
55+
56+
static void audit_fgets_state_init(struct audit_fgets_state *st)
57+
{
58+
st->buffer[0] = '\0';
59+
st->current = st->buffer;
60+
st->eptr = st->buffer + (2*BUF_SIZE);
61+
st->eof = 0;
62+
}
63+
64+
struct audit_fgets_state *audit_fgets_init(void)
65+
{
66+
struct audit_fgets_state *st = malloc(sizeof(*st));
67+
if (st)
68+
audit_fgets_state_init(st);
69+
return st;
70+
}
71+
72+
void audit_fgets_destroy(struct audit_fgets_state *st)
5073
{
51-
return eof;
74+
free(st);
75+
}
76+
77+
int audit_fgets_eof_r(struct audit_fgets_state *st)
78+
{
79+
return st->eof;
5280
}
5381

5482
/* This function dumps any accumulated text. This is to remove dangling text
5583
* that never got consumed for the intended purpose. */
56-
void audit_fgets_clear(void)
84+
void audit_fgets_clear_r(struct audit_fgets_state *st)
5785
{
58-
buffer[0] = 0;
59-
current = buffer;
60-
eof = 0;
86+
st->buffer[0] = 0;
87+
st->current = st->buffer;
88+
st->eof = 0;
6189
}
6290

6391
/* Function to check if we have more data stored
6492
* and ready to process. If we have a newline or enough
6593
* bytes we return 1 for success. Otherwise 0 meaning that
6694
* there is not enough to process without blocking. */
67-
int audit_fgets_more(size_t blen)
95+
int audit_fgets_more_r(struct audit_fgets_state *st, size_t blen)
6896
{
6997
size_t avail;
7098
char *nl;
7199

72100
assert(blen != 0);
73-
avail = current - buffer;
101+
avail = st->current - st->buffer;
74102

75103
/* only scan the valid region */
76-
nl = memchr(buffer, '\n', avail);
104+
nl = memchr(st->buffer, '\n', avail);
77105
return (nl || avail >= blen - 1);
78106
}
79107

@@ -82,42 +110,42 @@ int audit_fgets_more(size_t blen)
82110
* copy into buf, NUL-terminate, and return the number of chars.
83111
* It also returns 0 for no data. And -1 if there was an error reading
84112
* the fd. */
85-
int audit_fgets(char *buf, size_t blen, int fd)
113+
int audit_fgets_r(struct audit_fgets_state *st, char *buf, size_t blen, int fd)
86114
{
87-
size_t avail = current - buffer, line_len;
115+
size_t avail = st->current - st->buffer, line_len;
88116
char *line_end;
89117
ssize_t nread;
90118

91119
assert(blen != 0);
92120

93121
/* 1) Is there already a '\n' in the buffered data? */
94-
line_end = memchr(buffer, '\n', avail);
122+
line_end = memchr(st->buffer, '\n', avail);
95123

96124
/* 2) If not, and we still can read more, pull in more data */
97-
if (line_end == NULL && !eof && current != eptr) {
125+
if (line_end == NULL && !st->eof && st->current != st->eptr) {
98126
do {
99-
nread = read(fd, current, eptr - current);
127+
nread = read(fd, st->current, st->eptr - st->current);
100128
} while (nread < 0 && errno == EINTR);
101129

102130
if (nread < 0)
103131
return -1;
104132

105133
if (nread == 0)
106-
eof = 1;
134+
st->eof = 1;
107135
else {
108-
current[nread] = '\0';
109-
current += nread;
110-
avail += nread;
136+
st->current[nread] = '\0';
137+
st->current += nread;
138+
avail += nread;
111139
}
112140

113141
/* see if a newline arrived in that chunk */
114-
line_end = memchr(buffer, '\n', avail);
142+
line_end = memchr(st->buffer, '\n', avail);
115143
}
116144

117145
/* 3) Do we now have enough to return? */
118146
if (line_end == NULL) {
119147
/* not a full line—only return early if we still expect more */
120-
if (!eof && avail < blen - 1 && current != eptr)
148+
if (!st->eof && avail < blen - 1 && st->current != st->eptr)
121149
return 0;
122150

123151
/* else we’ll return whatever we have (either at EOF,
@@ -127,7 +155,7 @@ int audit_fgets(char *buf, size_t blen, int fd)
127155
/* 4) Compute how many chars to hand back */
128156
if (line_end) {
129157
/* include the '\n', but never exceed blen-1 */
130-
line_len = (line_end - buffer) + 1;
158+
line_len = (line_end - st->buffer) + 1;
131159
if (line_len > blen - 1)
132160
line_len = blen - 1;
133161

@@ -137,16 +165,48 @@ int audit_fgets(char *buf, size_t blen, int fd)
137165
line_len = (avail < blen - 1) ? avail : (blen - 1);
138166

139167
/* 5) Copy out, slide the remainder down, reset pointers */
140-
memcpy(buf, buffer, line_len);
168+
memcpy(buf, st->buffer, line_len);
141169
buf[line_len] = '\0';
142170

143171
size_t remainder = avail - line_len;
144172
if (remainder > 0)
145-
memmove(buffer, buffer + line_len, remainder);
173+
memmove(st->buffer, st->buffer + line_len, remainder);
146174

147-
current = buffer + remainder;
148-
*current = '\0';
175+
st->current = st->buffer + remainder;
176+
*st->current = '\0';
149177

150178
return (int)line_len;
151179
}
152180

181+
static inline void audit_fgets_ensure_global(void)
182+
{
183+
if (!global_init_done) {
184+
audit_fgets_state_init(&global_state);
185+
global_init_done = 1;
186+
}
187+
}
188+
189+
int audit_fgets_eof(void)
190+
{
191+
audit_fgets_ensure_global();
192+
return audit_fgets_eof_r(&global_state);
193+
}
194+
195+
void audit_fgets_clear(void)
196+
{
197+
audit_fgets_ensure_global();
198+
audit_fgets_clear_r(&global_state);
199+
}
200+
201+
int audit_fgets_more(size_t blen)
202+
{
203+
audit_fgets_ensure_global();
204+
return audit_fgets_more_r(&global_state, blen);
205+
}
206+
207+
int audit_fgets(char *buf, size_t blen, int fd)
208+
{
209+
audit_fgets_ensure_global();
210+
return audit_fgets_r(&global_state, buf, blen, fd);
211+
}
212+

lib/libaudit.h

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -305,12 +305,24 @@ int audit_can_control(void);
305305
int audit_can_write(void);
306306
int audit_can_read(void);
307307

308+
/* Opaque handle used by audit_fgets_r */
309+
typedef struct audit_fgets_state audit_fgets_state_t;
310+
308311
/* Helper function for reading stdin in plugins */
309312
void audit_fgets_clear(void);
310313
int audit_fgets_eof(void);
311314
int audit_fgets_more(size_t blen);
312315
int audit_fgets(char *buf, size_t blen, int fd)
313-
__attr_access ((__write_only__, 1, 2));
316+
__attr_access ((__write_only__, 1, 2));
317+
318+
void audit_fgets_destroy(audit_fgets_state_t *st);
319+
audit_fgets_state_t *audit_fgets_init(void)
320+
__attribute_malloc__ __attr_dealloc (audit_fgets_destroy, 1);
321+
void audit_fgets_clear_r(audit_fgets_state_t *st);
322+
int audit_fgets_eof_r(audit_fgets_state_t *st);
323+
int audit_fgets_more_r(audit_fgets_state_t *st, size_t blen);
324+
int audit_fgets_r(audit_fgets_state_t *st, char *buf, size_t blen, int fd)
325+
__attr_access ((__write_only__, 2, 3));
314326

315327
#ifdef __cplusplus
316328
}

0 commit comments

Comments
 (0)