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 }