optionsfrom: Properly handle errors when determining file size
[strongswan.git] / src / libstrongswan / utils / optionsfrom.c
1 /*
2 * Copyright (C) 2007-2008 Andreas Steffen
3 * Hochschule fuer Technik Rapperswil
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #include <stdio.h>
17 #include <errno.h>
18
19 #include <library.h>
20 #include <utils/debug.h>
21 #include <utils/lexparser.h>
22
23 #include "optionsfrom.h"
24
25 #define MAX_USES 20 /* loop-detection limit */
26 #define MORE_ARGS 10 /* first guess at how many arguments we'll need */
27
28 /*
29 * Defined in header.
30 */
31
32 typedef struct private_options_t private_options_t;
33
34 /**
35 * Private data of a options_t object.
36 */
37 struct private_options_t {
38 /**
39 * Public interface
40 */
41 options_t public;
42
43 /**
44 * reallocated argv array
45 */
46 char **newargv;
47
48 /**
49 * number of free arguments in newargv
50 */
51 int room;
52
53 /**
54 * number of included option files
55 */
56 int nuses;
57
58 /**
59 * allocated space for option files
60 */
61 char *buffers[MAX_USES];
62 };
63
64 METHOD(options_t, from, bool,
65 private_options_t *this, char *filename, int *argcp, char **argvp[], int optind)
66 {
67 int newargc;
68 int next; /* place for next argument */
69 char **newargv;
70 chunk_t src, line, token;
71 bool good = TRUE;
72 int linepos = 0;
73 FILE *fd;
74
75 /* avoid endless loops with recursive --optionsfrom arguments */
76 this->nuses++;
77 if (this->nuses >= MAX_USES)
78 {
79 DBG1(DBG_LIB, "optionsfrom called %d times by \"%s\" - looping?",
80 this->nuses + 1, (*argvp)[0]);
81 return FALSE;
82 }
83
84 fd = fopen(filename, "r");
85 if (fd == NULL)
86 {
87 DBG1(DBG_LIB, "optionsfrom: unable to open file '%s': %s",
88 filename, strerror(errno));
89 return FALSE;
90 }
91
92 /* determine the file size */
93 if (fseek(fd, 0, SEEK_END) == -1 || (src.len = ftell(fd)) == -1)
94 {
95 DBG1(DBG_LIB, "optionsfrom: unable to determine size of '%s': %s",
96 filename, strerror(errno));
97 fclose(fd);
98 return FALSE;
99 }
100 rewind(fd);
101
102 /* allocate one byte more just in case of a missing final newline */
103 src.ptr = this->buffers[this->nuses] = malloc(src.len + 1);
104
105 /* read the whole file into a chunk */
106 if (fread(src.ptr, 1, src.len, fd) != src.len)
107 {
108 DBG1(DBG_LIB, "optionsfrom: unable to read file '%s': %s",
109 filename, strerror(errno));
110 free(src.ptr);
111 fclose(fd);
112 return FALSE;
113 }
114 fclose(fd);
115
116 if (this->room)
117 {
118 newargc = *argcp;
119 newargv = malloc((newargc + 1 + this->room) * sizeof(char *));
120 }
121 else
122 {
123 newargc = *argcp + MORE_ARGS;
124 this->room = MORE_ARGS;
125 newargv = malloc((newargc + 1) * sizeof(char *));
126 }
127 memcpy(newargv, *argvp, optind * sizeof(char *));
128 next = optind;
129 newargv[next] = NULL;
130
131 while (fetchline(&src, &line) && good)
132 {
133 linepos++;
134 while (eat_whitespace(&line))
135 {
136 if (*line.ptr == '"'|| *line.ptr == '\'')
137 {
138 char delimiter = *line.ptr;
139
140 line.ptr++;
141 line.len--;
142 if (!extract_token(&token, delimiter, &line))
143 {
144 DBG1(DBG_LIB, "optionsfrom: missing terminator at %s:%d",
145 filename, linepos);
146 good = FALSE;
147 break;
148 }
149 }
150 else
151 {
152 if (!extract_token(&token, ' ', &line))
153 {
154 /* last token in a line */
155 token = line;
156 line.len = 0;
157 }
158 }
159
160 /* do we have to allocate more memory for additional arguments? */
161 if (this->room == 0)
162 {
163 newargc += MORE_ARGS;
164 newargv = realloc(newargv, (newargc + 1) * sizeof(char *));
165 this->room = MORE_ARGS;
166 }
167
168 /* terminate the token by replacing the delimiter with a null character */
169 *(token.ptr + token.len) = '\0';
170
171 /* assign the token to the next argument */
172 newargv[next] = token.ptr;
173 next++;
174 this->room--;
175 }
176 }
177
178 /* assign newargv to argv */
179 if (good)
180 {
181 memcpy(newargv + next, *argvp + optind, (*argcp + 1 - optind) * sizeof(char *));
182 *argcp += next - optind;
183 *argvp = newargv;
184 }
185
186 /* keep a pointer to the latest newargv and free any earlier version */
187 free(this->newargv);
188 this->newargv = newargv;
189
190 return good;
191 }
192
193 METHOD(options_t, destroy, void,
194 private_options_t *this)
195 {
196 while (this->nuses >= 0)
197 {
198 free(this->buffers[this->nuses--]);
199 }
200 free(this->newargv);
201 free(this);
202 }
203
204 /*
205 * Defined in header
206 */
207 options_t *options_create(void)
208 {
209 private_options_t *this;
210
211 INIT(this,
212 .public = {
213 .from = _from,
214 .destroy = _destroy,
215
216 },
217 .nuses = -1,
218 );
219
220 return &this->public;
221 }