1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2012, Daniil Lunev. All rights reserved.
23 */
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include "grubadm.h"
28 #include "error.h"
29 #include "menu.h"
30
31 char default_entry[MAX_STRING_SIZE] = { 0 };
32 char timeout[MAX_STRING_SIZE] = { 0 };
33 char serial[MAX_STRING_SIZE] = { 0 };
34 char terminal[MAX_STRING_SIZE] = { 0 };
35
36 const char * cmd_list[] = {
37 "entry_name",
38 "pool_label",
39 "pool_uuid",
40 "data_set",
41 "kernel_path",
42 "kernel_options",
43 "module",
44 "default_entry",
45 "timeout",
46 "serial",
47 "terminal",
48 NULL
49 };
50
51 void
52 free_menu (menu_list ** menu)
53 {
54 unsigned int i;
55 menu_list * iter = *menu;
56 menu_list * tmp = *menu;
57
58 const char fnc[] = "free_menu";
59
60 debug_print ("%s\n", fnc);
61
62 while (iter) {
63 if (iter->entry.modules_amount)
64 for (i = 0; i < iter->entry.modules_amount; ++i)
65 free (iter->entry.modules[i]);
66 free (iter->entry.modules);
67 tmp = iter;
68 iter = iter->next;
69 free (tmp);
70 }
71
72 *menu = NULL;
73 }
74
75 static menu_list *
76 new_entry (menu_list ** menu, const char * entry_name, const unsigned int num)
77 {
78 menu_list * added_entry;
79 menu_list * iter;
80
81 const char fnc[] = "new_entry";
82
83 debug_print ("%s\nargs : %s | %u\n", fnc, entry_name, num);
84
85 added_entry = (menu_list *) calloc (1, sizeof (menu_list));
86 if (! added_entry) {
87 print_system_error ();
88 return NULL;
89 }
90
91 memset ((void *) added_entry, 0, sizeof (menu_list));
92 added_entry->entry_number = num;
93 strcpy (added_entry->entry.entry_name, entry_name);
94 added_entry->entry.modules =
95 (char **) calloc (MAX_MODULE_AMOUNT, sizeof (char *));
96
97 if (! added_entry->entry.modules) {
98 free_menu (&added_entry);
99 print_system_error ();
100 return NULL;
101 }
102
103 if (! *menu) {
104 *menu = added_entry;
105 } else {
106 for (iter = *menu; iter->next; iter = iter->next)
107 if (! strcmp (iter->entry.entry_name, entry_name)) {
108 print_error ("%s %s", MSG_ERR_SAME_NAME, entry_name);
109 free_menu (&added_entry);
110 return NULL;
111 }
112 if (! strcmp (iter->entry.entry_name, entry_name)) {
113 print_error ("%s %s", MSG_ERR_SAME_NAME, entry_name);
114 free_menu (&added_entry);
115 return NULL;
116 }
117 iter->next = added_entry;
118 }
119
120 return added_entry;
121 }
122
123 menu_list *
124 add_entry (menu_list ** menu, menu_list * entry)
125 {
126 menu_list * iter;
127
128 const char fnc[] = "add_entry";
129
130 debug_print ("%s\n", fnc);
131
132 if (! entry)
133 return NULL;
134
135 if (! *menu) {
136 entry->entry_number = 0;
137 *menu = entry;
138 return entry;
139 }
140
141 for (iter = * menu; iter->next; iter = iter->next)
142 if (! strcmp (iter->entry.entry_name, entry->entry.entry_name))
143 return NULL;
144
145 if (! strcmp (iter->entry.entry_name, entry->entry.entry_name))
146 return NULL;
147
148 iter->next = entry;
149 entry->entry_number = iter->entry_number + 1;
150 return entry;
151 }
152
153 menu_list *
154 clone_entry (menu_list * entry)
155 {
156 unsigned int i;
157
158 char fnc[] = "clone_entry";
159
160 debug_print ("%s\n", fnc);
161
162 if (! entry)
163 return NULL;
164 menu_list * clone = (menu_list *) calloc (1, sizeof (menu_list));
165 memcpy ((void *) clone, (void *) entry, sizeof (menu_list));
166 clone->next = NULL;
167 clone->entry.modules = (char **) calloc (MAX_MODULE_AMOUNT, sizeof (char *));
168 for (i = 0; i < clone->entry.modules_amount; ++i)
169 clone->entry.modules[i] = strdup (entry->entry.modules[i]);
170 return clone;
171 }
172
173 menu_list *
174 enable_hyper (menu_list * entry)
175 {
176 unsigned int i;
177 char kernel_mod[MAX_STRING_SIZE];
178
179 char fnc[] = "enable_hyper";
180
181 debug_print ("%s\n", fnc);
182
183 if (! entry)
184 return NULL;
185
186 strcpy (kernel_mod, entry->entry.kernel);
187 strcat (kernel_mod, " ");
188 strcat (kernel_mod, entry->entry.kernel);
189 strcat (kernel_mod, " ");
190 strcat (kernel_mod, entry->entry.kernel_opts);
191
192 strcpy (entry->entry.kernel, HYPER_KERNEL);
193 strcpy (entry->entry.kernel_opts, HYPER_KERNEL_OPTS);
194
195 for (i = entry->entry.modules_amount; i > 0; --i)
196 entry->entry.modules[i] = entry->entry.modules[i-1];
197 entry->entry.modules[0] = strdup (kernel_mod);
198 ++(entry->entry.modules_amount);
199
200 return entry;
201 }
202
203 menu_list *
204 disable_hyper (menu_list * entry)
205 {
206 unsigned int i;
207 char * tmp;
208
209 char fnc[] = "disable_hyper";
210
211 debug_print ("%s\n", fnc);
212
213 if (! entry)
214 return NULL;
215
216 tmp = strchr (entry->entry.modules[0], ' ');
217 if (! tmp)
218 return NULL;
219
220 *tmp++ = '\0';
221
222 strcpy (entry->entry.kernel, entry->entry.modules[0]);
223
224 entry->entry.kernel_opts[0] = '\0';
225 tmp = strchr(tmp, ' ');
226 if (tmp && *(++tmp)) {
227 strcpy (entry->entry.kernel_opts, tmp);
228 }
229 free (entry->entry.modules[0]);
230 --(entry->entry.modules_amount);
231
232 for (i = 0; i < entry->entry.modules_amount; ++i)
233 entry->entry.modules[i] = entry->entry.modules[i + 1];
234
235 return entry;
236 }
237
238 int
239 delete_entry (menu_list ** menu, menu_list * del)
240 {
241 menu_list * iter = *menu;
242 int d_num, i_num;
243
244 const char fnc[] = "delete_entry";
245
246 debug_print ("%s\n", fnc);
247
248 if (! menu || ! del)
249 return 0;
250
251 i_num = del->entry_number;
252
253 if (iter == del) {
254 *menu = (*menu)->next;
255 iter->next = NULL;
256 free_menu (&iter);
257 } else {
258 while (iter->next) {
259 if (iter->next == del) {
260 iter->next = del->next;
261 del->next = NULL;
262 free_menu (&del);
263 break;
264 }
265 iter = iter->next;
266 }
267 }
268
269 sscanf (default_entry, "%d", &d_num);
270
271 if (i_num <= d_num) {
272 d_num = d_num ? d_num - 1 : 0;
273 sprintf (default_entry, "%d", d_num);
274 }
275
276 return 0;
277 }
278
279 static menu_list *
280 add_param (menu_list ** menu,
281 const cmd_id id,
282 const char * value,
283 unsigned int * num)
284 {
285 menu_list * iter;
286 char * tmp;
287
288 const char fnc[] = "add_param";
289
290 debug_print ("%s\narg: %u | %s | %u\n", fnc, (unsigned int) id, value, *num);
291
292 if (id == CMD_ENTRY_NAME)
293 return new_entry (menu, value, (*num)++);
294
295 if (! *menu) {
296 print_error ("%s, %s", MSG_ERR_WRONG_SYNTAX, "entry isn't defined");
297 }
298
299 for (iter = * menu; iter->next; iter = iter->next)
300 ;
301
302 switch (id) {
303 case CMD_POOL_LABEL:
304 strcpy (iter->entry.pool_label, value);
305 break;
306 case CMD_POOL_UUID:
307 strcpy (iter->entry.pool_uuid, value);
308 break;
309 case CMD_DATA_SET:
310 strcpy (iter->entry.dataset, value);
311 break;
312 case CMD_KERNEL_PATH:
313 strcpy (iter->entry.kernel, value);
314 break;
315 case CMD_KERNEL_OPTIONS:
316 strcpy (iter->entry.kernel_opts, value);
317 break;
318 case CMD_BA_PATH:
319 if (iter->entry.modules_amount >= MAX_MODULE_AMOUNT)
320 return NULL;
321 iter->entry.modules[iter->entry.modules_amount] =
322 (char *) malloc (MAX_STRING_SIZE);
323 if (! iter->entry.modules[iter->entry.modules_amount])
324 return NULL;
325 strcpy (iter->entry.modules[iter->entry.modules_amount], value);
326 ++(iter->entry.modules_amount);
327 break;
328 default:
329 return NULL;
330 }
331 return iter;
332 }
333
334 void
335 list_menu (menu_list * menu)
336 {
337 const char fnc[] = "list_menu";
338
339 debug_print ("%s\n", fnc);
340
341 if (*default_entry)
342 printf ("%s%s%s\n", cmd_list[CMD_DEFAULT], SEPARATOR, default_entry);
343
344 if (*timeout)
345 printf ("%s%s%s\n", cmd_list[CMD_TIMEOUT], SEPARATOR, timeout);
346
347 if (*serial)
348 printf ("%s%s%s\n", cmd_list[CMD_SERIAL], SEPARATOR, serial);
349
350 if (*terminal)
351 printf ("%s%s%s\n", cmd_list[CMD_TERMINAL], SEPARATOR, terminal);
352
353 while (menu) {
354 printf("%u : %s\n", menu->entry_number, menu->entry.entry_name);
355 menu = menu->next;
356 }
357 }
358
359 menu_list *
360 find_entry (menu_list * menu, menu_list * find)
361 {
362 unsigned int i = 0;
363 const char fnc[] = "find_entry";
364
365 debug_print ("%s\n", fnc);
366
367 if (! menu || ! find)
368 return NULL;
369
370 while (menu) {
371 if (find->entry.entry_name[0])
372 if (strcmp (menu->entry.entry_name, find->entry.entry_name))
373 goto out;
374 if (find->entry.pool_label[0])
375 if (strcmp (menu->entry.pool_label, find->entry.pool_label))
376 goto out;
377 if (find->entry.pool_uuid[0])
378 if (strcmp (menu->entry.pool_uuid, find->entry.pool_uuid))
379 goto out;
380 if (find->entry.dataset[0])
381 if (strcmp (menu->entry.dataset, find->entry.dataset))
382 goto out;
383 if (find->entry.kernel[0])
384 if (strcmp (menu->entry.kernel, find->entry.kernel))
385 goto out;
386
387 if (find->entry.modules_amount) {
388 for (i = 0; i < menu->entry.modules_amount; ++i)
389 if (! strcmp (menu->entry.modules[i], find->entry.modules[0]))
390 return menu;
391 goto out;
392 }
393
394 return menu;
395 out:
396 menu = menu->next;
397 }
398
399 return NULL;
400 }
401
402 menu_list *
403 get_entry_by_number (menu_list * menu, int number)
404 {
405 while (menu)
406 if (menu->entry_number == number)
407 return menu;
408 else
409 menu = menu->next;
410 return NULL;
411 }
412
413 static size_t
414 get_line (FILE * menu_file, char * line)
415 {
416 const char fnc[] = "get_line";
417
418 debug_print ("%s\n", fnc);
419
420 if (! fgets (line, MAX_STRING_SIZE, menu_file)) {
421 if (! feof (menu_file))
422 print_system_error ();
423 return -1;
424 }
425
426 if ((line[strlen (line) - 1] != '\n') && (! feof (menu_file))) {
427 print_error ("%s (> %d)", MSG_ERR_LONG_LINE, MAX_STRING_SIZE);
428 return -1;
429 }
430
431 line[strlen (line) - 1] = '\0';
432
433 return strlen (line);
434 }
435
436 static cmd_id
437 get_param_id (const char * param)
438 {
439 unsigned int id;
440
441 const char fnc[] = "get_param_id";
442
443 debug_print ("%s\narg : %s\n", fnc, param);
444
445 for (id = 0; cmd_list[id]; ++id)
446 if (! strcmp (cmd_list[id], param))
447 break;
448
449 return id;
450 }
451
452 menu_list *
453 parse_file (const char * file_name, int * entries_amount)
454 {
455 FILE * menu_file = NULL;
456 unsigned int counter = 0;
457 menu_list * menu = NULL;
458 char line[MAX_STRING_SIZE];
459 char * param;
460 char * value;
461 cmd_id id;
462
463 const char fnc[] = "parse_file";
464
465 debug_print ("%s\narg : %s\n", fnc, file_name);
466
467 menu_file = fopen (file_name, "r");
468 if (! menu_file) {
469 print_system_error ();
470 goto out;
471 }
472
473 for (;;) {
474 if (get_line (menu_file, line) == -1) {
475 if (feof (menu_file))
476 break;
477 free_menu (&menu);
478 counter = -1;
479 goto out;
480 }
481
482 if (line[0] == '#')
483 continue;
484
485 param = line;
486 while ((*param == ' ') || (*param == '\t'))
487 ++ param;
488 if (*param == '\0')
489 continue;
490
491 value = strchr (line, '=');
492 if (! value) {
493 print_error ("%s at line '%s'", MSG_ERR_WRONG_SYNTAX, line);
494 free_menu (&menu);
495 counter = -1;
496 goto out;
497 }
498 *value++ = '\0';
499
500 id = get_param_id (param);
501 if (id == CMD_UNKNOWN) {
502 print_error ("%s at line '%s'", MSG_ERR_WRONG_SYNTAX, line);
503 free_menu (&menu);
504 counter = -1;
505 goto out;
506 }
507
508 if (id == CMD_DEFAULT) {
509 strcpy (default_entry, value);
510 } else if (id == CMD_TIMEOUT) {
511 strcpy (timeout, value);
512 } else if (id == CMD_SERIAL) {
513 strcpy (serial, value);
514 } else if (id == CMD_TERMINAL) {
515 strcpy (terminal, value);
516 } else if (! add_param (&menu, id, value, &counter)) {
517 free_menu (&menu);
518 counter = -1;
519 goto out;
520 }
521 }
522
523 out:
524 if (menu_file)
525 fclose (menu_file);
526
527 *entries_amount = counter;
528
529 return menu;
530 }
531
532 int
533 write_menu (const char * config, const char * tmp_config, menu_list * menu)
534 {
535 FILE * tmp_menu = fopen (tmp_config, "w");
536 unsigned int i = 0;
537
538 const char fnc[] = "write_menu";
539
540 debug_print ("%s\n", fnc);
541
542 if (! tmp_menu) {
543 print_system_error();
544 return 0;
545 }
546
547 if (default_entry[0])
548 fprintf (tmp_menu, "%s%s%s\n", cmd_list[CMD_DEFAULT], SEPARATOR, default_entry);
549 if (timeout[0])
550 fprintf (tmp_menu, "%s%s%s\n\n", cmd_list[CMD_TIMEOUT], SEPARATOR, timeout);
551 if (serial[0])
552 fprintf (tmp_menu, "%s%s%s\n\n", cmd_list[CMD_SERIAL], SEPARATOR, serial);
553 if (terminal[0])
554 fprintf (tmp_menu, "%s%s%s\n\n", cmd_list[CMD_TERMINAL], SEPARATOR, terminal);
555
556 while (menu) {
557 fprintf (tmp_menu, "%s%s%s\n", cmd_list[CMD_ENTRY_NAME], SEPARATOR, menu->entry.entry_name);
558 if (menu->entry.pool_label[0])
559 fprintf (tmp_menu, "%s%s%s\n", cmd_list[CMD_POOL_LABEL], SEPARATOR, menu->entry.pool_label);
560 if (menu->entry.pool_uuid[0])
561 fprintf (tmp_menu, "%s%s%s\n", cmd_list[CMD_POOL_UUID], SEPARATOR, menu->entry.pool_uuid);
562 if (menu->entry.dataset[0])
563 fprintf (tmp_menu, "%s%s%s\n", cmd_list[CMD_DATA_SET], SEPARATOR, menu->entry.dataset);
564 if (menu->entry.kernel[0])
565 fprintf (tmp_menu, "%s%s%s\n", cmd_list[CMD_KERNEL_PATH], SEPARATOR, menu->entry.kernel);
566 if (menu->entry.kernel_opts[0])
567 fprintf (tmp_menu, "%s%s%s\n", cmd_list[CMD_KERNEL_OPTIONS], SEPARATOR, menu->entry.kernel_opts);
568 if (menu->entry.modules_amount)
569 for (i = 0; i < menu->entry.modules_amount; ++i)
570 fprintf (tmp_menu, "%s%s%s\n", cmd_list[CMD_BA_PATH], SEPARATOR, menu->entry.modules[i]);
571 fprintf (tmp_menu, "#----------------------------------------------------\n");
572 menu = menu->next;
573 }
574
575 fclose (tmp_menu);
576
577 remove (config);
578 rename (tmp_config, config);
579
580 return 1;
581 }