forked from tests-always-included/libnss_exec
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathnss_exec.c
316 lines (247 loc) · 7.3 KB
/
nss_exec.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
#define NSS_EXEC_C
#include "nss_exec.h"
#include <math.h>
#include <limits.h>
#include <signal.h>
#define IS_WHITESPACE(c) ((c) == ' ' || (c) == '\t')
#define IS_FIELD_SEPARATOR(c) ((c) == ':' || (c) == '\0')
/**
* Initialize a field_parse_info struct
*/
void field_parse_init(field_parse_info *info, char *buffer, size_t buffer_length, char *output) {
memset(buffer, '\0', buffer_length);
info->buffer_start = buffer;
info->buffer_left = buffer_length;
info->output = output;
info->output_offset = 0;
info->return_code = 0;
}
/**
* Parse out a single string, copy it to the buffer and return a pointer
* to where this string starts in the buffer.
*/
char *field_parse_string(field_parse_info *info) {
size_t i, len;
char *copy;
// Take no action if we have already hit an error
if (info->return_code) {
return NULL;
}
// Determine length of data to copy
i = info->output_offset;
len = 0;
while (! IS_FIELD_SEPARATOR(info->output[i])) {
i ++;
len ++;
}
// Confirm space is available in the buffer
if (len + 1 > info->buffer_left) {
info->return_code = -2;
return NULL;
}
// Copy
copy = info->buffer_start;
i = info->output_offset;
while (len --) {
info->buffer_start[0] = info->output[i];
info->buffer_start ++;
i += 1;
}
// Null terminate the copy
info->buffer_start[0] = '\0';
info->buffer_start ++;
// Update output_offset
if (info->output[i] == ':') {
i += 1;
}
info->output_offset = i;
return copy;
}
/**
* Take a string field and split it into separate strings, delimited by
* spaces. Then return a pointer to a list of pointers, like how one does
* array things like this in C.
*/
char **field_parse_string_array(field_parse_info *info) {
int i, field_count, fields_length, size;
char **split, **return_value;
field_count = 0; // Number of elements in the array
fields_length = 0; // Total string size of array elements including nulls.
i = info->output_offset; // Generic character counter
// Bypass initial whitespace
while (IS_WHITESPACE(info->output[i])) {
i += 1;
}
while (! IS_FIELD_SEPARATOR(info->output[i])) {
field_count += 1;
// Grab field
while (! IS_FIELD_SEPARATOR(info->output[i]) && ! IS_WHITESPACE(info->output[i])) {
fields_length += 1;
i += 1;
}
fields_length += 1; // For the eventual NULL byte
// Bypass whitespace
while (IS_WHITESPACE(info->output[i])) {
i += 1;
}
}
// Size is the amount of data used for the pointers to strings.
size = (field_count + 1) * sizeof(char *); // Add one for the null pointer
if (fields_length + size > info->buffer_left) {
info->return_code = -2;
return NULL;
}
// Just take care of buffer_left right away
info->buffer_left -= size + fields_length;
// Allocate the array of pointers
split = (char **) info->buffer_start;
return_value = split; // We move `split` forward
info->buffer_start += size;
i = info->output_offset;
// Bypass initial whitespace
while (IS_WHITESPACE(info->output[i])) {
i += 1;
}
while (! IS_FIELD_SEPARATOR(info->output[i])) {
*split = info->buffer_start;
split ++;
// Grab field
while (! IS_FIELD_SEPARATOR(info->output[i]) && ! IS_WHITESPACE(info->output[i])) {
info->buffer_start[0] = info->output[i];
info->buffer_start += 1;
i += 1;
}
info->buffer_start[0] = '\0';
info->buffer_start += 1;
// Bypass whitespace
while (IS_WHITESPACE(info->output[i])) {
i += 1;
}
}
return return_value;
}
/**
* Parse out a long integer and return the value.
*/
long field_parse_long(field_parse_info *info) {
int i;
long sign = 1, result = 0;
// Take no action if we have already hit an error
if (info->return_code) {
return -1;
}
i = info->output_offset;
// Detect a sign - not sure if it is valid in other parts of the system.
if (info->output[i] == '-') {
sign = -1;
i ++;
}
// Consume all numbers
while (! IS_FIELD_SEPARATOR(info->output[i]) && info->output[i] >= '0' && info->output[i] <= '9') {
result *= 10;
result += info->output[i] - '0';
i ++;
}
// Skip letters, symbols, etc that are left in the field
while (! IS_FIELD_SEPARATOR(info->output[i])) {
i ++;
}
// Get past the colon
if (info->output[i] == ':') {
i ++;
}
info->output_offset = i;
return result * sign;
}
/**
* Returns a truthy value if there is data for another field (even if
* that means the field is empty)
*/
int field_parse_more(field_parse_info *info) {
// Check for errors
if (info->return_code) {
return 0;
}
// This field is empty, but we progress past
if (info->output[info->output_offset] == ':') {
info->output_offset += 1;
return 0;
}
// This field is not empty
if (info->output[info->output_offset] != '\0') {
return 1;
}
// At NULL so there are no more fields
return 0;
}
/**
* Common function to change the return code and set errnop based on
* field parsing results.
*/
enum nss_status handle_pack_result(int pack_result, int *errnop) {
if (pack_result == -1) {
if (errnop) {
*errnop = ENOENT;
}
return NSS_STATUS_UNAVAIL;
}
if (pack_result != 0) {
if (errnop) {
*errnop = ERANGE;
}
return NSS_STATUS_TRYAGAIN;
}
return NSS_STATUS_SUCCESS;
}
/**
* Execute a script.
*
* This version can pass additional data as a string.
*/
enum nss_status nss_exec_script(char **output, char *command_code, const char *data) {
char command[1024];
static char line[1024];
FILE *fp;
int i, resultCode;
struct sigaction oldSignalAction;
// Initialize
if (output) {
*output = NULL;
}
snprintf(command, 1024, "%s %s %s", NSS_EXEC_SCRIPT, command_code, data ? data : "");
command[1024] = '\0'; // Ensure there's a null at the end
fp = popen(command, "r");
fgets(line, 1024, fp);
sigaction(SIGCHLD, NULL, &oldSignalAction);
signal(SIGCHLD, SIG_DFL);
resultCode = WEXITSTATUS(pclose(fp));
sigaction(SIGCHLD, &oldSignalAction, NULL);
if (output) {
// Cleanse out newlines and copy
for (i = 0; line[i]; i += 1) {
if (line[i] == '\n' || line[i] == '\r') {
line[i] = '\0';
}
}
*output = line;
}
if (resultCode == 0) {
return NSS_STATUS_SUCCESS;
}
if (resultCode == 1) {
return NSS_STATUS_NOTFOUND;
}
if (resultCode == 2) {
return NSS_STATUS_TRYAGAIN;
}
return NSS_STATUS_UNAVAIL;
}
/**
* Same as nss_exec_script, but this version takes a long integer and will
* convert it to a string and then call the original function.
*/
enum nss_status nss_exec_script_long(char **output, char *command, long data) {
char dataStr[(int) ceil(log10(ULONG_MAX)) + 1];
sprintf(dataStr, "%ld", data);
return nss_exec_script(output, command, dataStr);
}