lib: Add pb-config module
[petitboot] / lib / pb-config / storage-powerpc-nvram.c
1
2 #include <string.h>
3 #include <stdlib.h>
4 #include <sys/types.h>
5 #include <sys/wait.h>
6
7 #include <talloc/talloc.h>
8 #include <list/list.h>
9 #include <log/log.h>
10
11 #include "pb-config.h"
12 #include "storage.h"
13
14 static const char *partition = "common";
15 static const char *prefix = "petitboot,";
16
17 struct param {
18         char                    *name;
19         char                    *value;
20         bool                    modified;
21         struct list_item        list;
22 };
23
24 struct powerpc_nvram_storage {
25         struct config_storage   storage;
26         struct list             params;
27 };
28
29 #define to_powerpc_nvram_storage(s) \
30         container_of(s, struct powerpc_nvram_storage, storage)
31
32 /* a partition max a max size of 64k * 16bytes = 1M */
33 static const int max_partition_size = 64 * 1024 * 16;
34
35 static int parse_nvram_params(struct powerpc_nvram_storage *nv,
36                 char *buf, int len)
37 {
38         char *pos, *name, *value;
39         unsigned int paramlen;
40         int i, count;
41
42         /* discard 2 header lines:
43          * "common" partiton"
44          * ------------------
45          */
46         pos = buf;
47         count = 0;
48
49         for (i = 0; i < len; i++) {
50                 if (pos[i] == '\n')
51                         count++;
52                 if (count == 2)
53                         break;
54         }
55
56         if (i == len) {
57                 fprintf(stderr, "failure parsing nvram output\n");
58                 return -1;
59         }
60
61         for (pos = buf + i; pos < buf + len; pos += paramlen) {
62                 unsigned int namelen;
63                 struct param *param;
64
65                 paramlen = strlen(pos);
66
67                 name = pos;
68                 value = strchr(pos, '=');
69                 if (!value)
70                         continue;
71
72                 namelen = name - value;
73                 if (namelen <= strlen(prefix))
74                         continue;
75
76                 if (strncmp(name, prefix, strlen(prefix)))
77                         continue;
78
79                 name += strlen(prefix);
80                 value++;
81
82                 param = talloc(nv, struct param);
83                 param->modified = false;
84                 param->name = talloc_strndup(nv, name, namelen);
85                 param->value = talloc_strdup(nv, value);
86                 list_add(&nv->params, &param->list);
87         }
88
89         return 0;
90 }
91
92 static int parse_nvram(struct powerpc_nvram_storage *nv)
93 {
94         int rc, len, buf_len;
95         int pipefds[2], status;
96         char *buf;
97         pid_t pid;
98
99         rc = pipe(pipefds);
100         if (rc) {
101                 perror("pipe");
102                 return -1;
103         }
104
105         pid = fork();
106
107         if (pid < 0) {
108                 perror("fork");
109                 return -1;
110         }
111
112         if (pid == 0) {
113                 close(STDIN_FILENO);
114                 close(pipefds[0]);
115                 dup2(pipefds[1], STDOUT_FILENO);
116                 execlp("nvram", "nvram", "--print-config",
117                                 "--partition", partition, NULL);
118                 exit(EXIT_FAILURE);
119         }
120
121         close(pipefds[1]);
122
123         len = 0;
124         buf_len = max_partition_size;
125         buf = talloc_array(nv, char, buf_len);
126
127         for (;;) {
128                 rc = read(pipefds[0], buf + len, buf_len - len);
129
130                 if (rc < 0) {
131                         perror("read");
132                         break;
133                 }
134
135                 if (rc == 0)
136                         break;
137
138                 len += rc;
139         }
140
141         waitpid(pid, &status, 0);
142         if (!WIFEXITED(status) || WEXITSTATUS(status)) {
143                 fprintf(stderr, "nvram process returned "
144                                 "non-zero exit status\n");
145                 return -1;
146         }
147
148         if (rc < 0)
149                 return rc;
150
151         return parse_nvram_params(nv, buf, len);
152 }
153
154 static const char *get_param(struct powerpc_nvram_storage *nv,
155                 const char *name)
156 {
157         struct param *param;
158
159         list_for_each_entry(&nv->params, param, list)
160                 if (!strcmp(param->name, name))
161                         return param->value;
162         return NULL;
163 }
164
165 static int parse_hwaddr(struct network_config *config, char *str)
166 {
167         int i;
168
169         if (strlen(str) != strlen("00:00:00:00:00:00"))
170                 return -1;
171
172         for (i = 0; i < HWADDR_SIZE; i++) {
173                 char byte[3], *endp;
174                 unsigned long x;
175
176                 byte[0] = str[i * 3 + 0];
177                 byte[1] = str[i * 3 + 1];
178                 byte[2] = '\0';
179
180                 x = strtoul(byte, &endp, 16);
181                 if (endp != byte + 2)
182                         return -1;
183
184                 config->hwaddr[i] = x & 0xff;
185         }
186
187         return 0;
188 }
189
190 static int parse_one_network_config(struct network_config *config,
191                 char *confstr)
192 {
193         char *tok, *saveptr;
194
195         if (!confstr || !strlen(confstr))
196                 return -1;
197
198         /* first token should be the mac address */
199         tok = strtok_r(confstr, ",", &saveptr);
200         if (!tok)
201                 return -1;
202
203         if (parse_hwaddr(config, tok))
204                 return -1;
205
206         /* second token is the method */
207         tok = strtok_r(NULL, ",", &saveptr);
208         if (!tok || !strlen(tok) || !strcmp(tok, "ignore")) {
209                 config->ignore = true;
210                 return 0;
211         }
212
213         if (!strcmp(tok, "dhcp")) {
214                 config->method = CONFIG_METHOD_DHCP;
215
216         } else if (!strcmp(tok, "static")) {
217                 config->method = CONFIG_METHOD_STATIC;
218
219                 /* ip/mask, [optional] gateway, [optional] dns */
220                 tok = strtok_r(NULL, ",", &saveptr);
221                 if (!tok)
222                         return -1;
223                 config->static_config.address =
224                         talloc_strdup(config, tok);
225
226                 tok = strtok_r(NULL, ",", &saveptr);
227                 if (tok) {
228                         config->static_config.gateway =
229                                 talloc_strdup(config, tok);
230                         tok = strtok_r(NULL, ",", &saveptr);
231                 }
232
233                 if (tok) {
234                         config->static_config.dns =
235                                 talloc_strdup(config, tok);
236                 }
237         } else {
238                 pb_log("Unknown network configuration method %s\n", tok);
239                 return -1;
240         }
241
242         return 0;
243 }
244
245 static void populate_network_config(struct powerpc_nvram_storage *nv,
246                 struct config *config)
247 {
248         const char *cval;
249         char *val;
250         int i;
251
252         cval = get_param(nv, "network");
253         if (!cval || !strlen(cval))
254                 return;
255
256         val = talloc_strdup(config, cval);
257
258         for (i = 0; ; i++) {
259                 struct network_config *netconf;
260                 char *tok, *saveptr;
261                 int rc;
262
263                 tok = strtok_r(i == 0 ? val : NULL, " ", &saveptr);
264                 if (!tok)
265                         break;
266
267                 netconf = talloc(nv, struct network_config);
268
269                 rc = parse_one_network_config(netconf, tok);
270                 if (rc) {
271                         talloc_free(netconf);
272                         continue;
273                 }
274
275                 config->network_configs = talloc_realloc(nv,
276                                                 config->network_configs,
277                                                 struct network_config *,
278                                                 ++config->n_network_configs);
279
280                 config->network_configs[config->n_network_configs - 1] =
281                                                 netconf;
282         }
283
284         talloc_free(val);
285 }
286
287 static void populate_config(struct powerpc_nvram_storage *nv,
288                 struct config *config)
289 {
290         const char *val;
291
292         /* if the "auto-boot?' property is present and "false", disable auto
293          * boot */
294         val = get_param(nv, "auto-boot?");
295         config->autoboot_enabled = !val || strcmp(val, "false");
296
297         populate_network_config(nv, config);
298 }
299
300 static int load(struct config_storage *st, struct config *config)
301 {
302         struct powerpc_nvram_storage *nv = to_powerpc_nvram_storage(st);
303         int rc;
304
305         rc = parse_nvram(nv);
306         if (rc)
307                 return rc;
308
309         populate_config(nv, config);
310
311         return 0;
312 }
313
314 struct config_storage *create_powerpc_nvram_storage(void *ctx)
315 {
316         struct powerpc_nvram_storage *nv;
317
318         nv = talloc(ctx, struct powerpc_nvram_storage);
319         nv->storage.load = load;
320         list_init(&nv->params);
321
322         return &nv->storage;
323 }