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 <unistd.h>
  28 #include <getopt.h>
  29 #include <assert.h>
  30 #include "grubadm.h"
  31 #include "error.h"
  32 #include "menu.h"
  33 
  34 menu_list get_arg = { 0 };
  35 menu_list find_arg = { 0 };
  36 menu_list set_arg = { 0 };
  37 int get_timeout = 0;
  38 int get_default = 0;
  39 int get_serial = 0;
  40 int get_terminal = 0;
  41 int lst_flag = 0;
  42 int new_flag = 0;
  43 int all_flag = 0;
  44 int del_flag = 0;
  45 int fnd_flag = 0;
  46 int hlp_flag = 0;
  47 int get_flag = 0;
  48 int set_flag = 0;
  49 int cln_flag = 0;
  50 int clr_flag = 0;
  51 int enh_flag = 0;
  52 int dih_flag = 0;
  53 int num_flag = 0;
  54 int number;
  55 char * set_timeout = NULL;
  56 char * set_default = NULL;
  57 char * set_serial = NULL;
  58 char * set_terminal = NULL;
  59 char * alt_root = NULL;
  60 
  61 static char opt_string[] = "landh?R:";
  62 static const struct option options[] = {
  63         { "list", no_argument, NULL, 'l' },
  64         { "all", no_argument, NULL, 'a' },
  65         { "new", no_argument, NULL, 'n' },
  66         { "delete", no_argument, NULL, 'd' },
  67         { "help", no_argument, NULL, 'h' },
  68         { "name", required_argument, NULL, 4 },
  69         { "pool", required_argument, NULL, 5 },
  70         { "uuid", required_argument, NULL, 6 },
  71         { "dataset", required_argument, NULL, 7 },
  72         { "kernel", required_argument, NULL, 8 },
  73         { "module", required_argument, NULL, 9 },
  74         { "set-name", required_argument, NULL, 10 },
  75         { "set-pool", required_argument, NULL, 11 },
  76         { "set-uuid", required_argument, NULL, 12 },
  77         { "set-dataset", required_argument, NULL, 13 },
  78         { "set-kernel", required_argument, NULL, 14 },
  79         { "set-opts", required_argument, NULL, 15 },
  80         { "set-module", required_argument, NULL, 16 },
  81         { "set-default", required_argument, NULL, 17 },
  82         { "set-timeout", required_argument, NULL, 18 },
  83         { "get-name", no_argument, NULL, 19 },
  84         { "get-pool", no_argument, NULL, 20 },
  85         { "get-uuid", no_argument, NULL, 21 },
  86         { "get-dataset", no_argument, NULL, 22 },
  87         { "get-kernel", no_argument, NULL, 23 },
  88         { "get-opts", no_argument, NULL, 24 },
  89         { "get-modules", no_argument, NULL, 25 },
  90         { "get-default", no_argument, NULL, 26 },
  91         { "get-timeout", no_argument, NULL, 27 },
  92         { "get-all", no_argument, NULL, 28 },
  93         { "alt-root", required_argument, NULL, 'R' },
  94         { "default", no_argument, NULL, 30 },
  95         { "set-serial", required_argument, NULL, 31 },
  96         { "set-terminal", required_argument, NULL, 32 },
  97         { "get-serial", no_argument, NULL, 33 },
  98         { "get-terminal", no_argument, NULL, 34 },
  99         { "clone", no_argument, NULL, 35 },
 100         { "enable-hyper", no_argument, NULL, 36 },
 101         { "disable-hyper", no_argument, NULL, 37 },
 102         { "clear", no_argument, NULL, 38 },
 103         { "number", required_argument, NULL, 39 },
 104         { NULL, no_argument, NULL, 0 },
 105 };
 106 
 107 static void
 108 usage ()
 109 {
 110         printf (
 111                 "Usage:\n"
 112                 "--list list all entries\n"
 113                 "--all apply actions to all appropriate entries\n"
 114                 "--new create new entry\n"
 115                 "--delete delete entry\n"
 116                 "--clone clone existing entry\n"
 117                 "--default set default entry properties\n"
 118                 "--help show this message\n"
 119                 "--number <opt> find entry by number (-1 = default entry)\n"
 120                 "--name <opt> find entry by name\n"
 121                 "--pool <opt> by pool name\n"
 122                 "--uuid <opt> by uuid\n"
 123                 "--dataset <opt> by dataset\n"
 124                 "--kernel <opt> by kernel\n"
 125                 "--module <opt> by module\n"
 126                 "--set-name <opt> set new name to entry\n"
 127                 "--set-pool <opt>\n"
 128                 "--set-uuid <opt>\n"
 129                 "--set-kernel <opt>\n"
 130                 "--set-opts <opt>\n"
 131                 "--set-module <opt>\n"
 132                 "--set-default <opt>\n"
 133                 "--set-timeout <opt>\n"
 134                 "--get-name get entry name\n"
 135                 "--get-pool\n"
 136                 "--get-uuid\n"
 137                 "--get-kernel\n"
 138                 "--get-opts\n"
 139                 "--get-module\n"
 140                 "--get-default\n"
 141                 "--get-timeout\n"
 142                 "--get-all retrieve all params of entry\n"
 143                 "--alt-root <opt> alternate root directory\n"
 144                 "--default set default fields value to entry\n"
 145                 "--set-serial set params of serial port\n"
 146                 "--set-terminal set default terminal\n"
 147                 "--get-serial get params of serial port"
 148                 "--get-terminal get default terminal\n"
 149                 "--enable-hyper convert entry to boot with xen\n"
 150                 "--disable-hyper convert entry to normal boot\n"
 151                 "--clear delete all entries\n"
 152         );
 153 }
 154 
 155 static char * 
 156 get_pool (const char * file)
 157 {
 158         char buf[MAX_STRING_SIZE];
 159         char * tmp;
 160         FILE * pipe;
 161 
 162         const char fnc[] = "get_pool";
 163 
 164         debug_print ("%s\narg: %s\n", fnc, file);
 165 
 166         strcpy (buf, "/usr/bin/df -h ");
 167         strcat (buf, file);
 168         
 169         pipe = popen (buf, "r");
 170         if (! pipe)
 171                 return NULL;
 172 
 173         fgets (buf, MAX_STRING_SIZE, pipe);
 174         if (! fgets (buf, MAX_STRING_SIZE, pipe)) {
 175                 pclose (pipe);
 176                 return NULL;
 177         }
 178 
 179         pclose (pipe);
 180         tmp = strchr (buf, '/');
 181         *tmp = 0;
 182         tmp = strdup (buf);
 183         return tmp;
 184 }
 185 
 186 static int
 187 parse_args (int argc, char ** argv)
 188 {
 189         int opt = 0;
 190         int index;
 191         
 192         const char fnc[] = "parse_args";
 193 
 194         debug_print ("%s\n", fnc);
 195 
 196         while ((opt = getopt_long (argc, argv, opt_string, options, &index)) != -1) {
 197                 switch (opt) {
 198                 case 'a':
 199                         ++all_flag;
 200                         break;
 201                 case 'd':
 202                         ++del_flag;
 203                         break;
 204                 case 'l':
 205                         ++lst_flag;
 206                         break;
 207                 case 'n':
 208                         ++new_flag;
 209                         break;
 210                 case 'h':
 211                 case '?':
 212                         ++hlp_flag;
 213                         break;
 214                 case 4:
 215                         ++fnd_flag;
 216                         strcpy (find_arg.entry.entry_name, optarg);
 217                         break;
 218                 case 5:
 219                         ++fnd_flag;
 220                         strcpy (find_arg.entry.pool_label, optarg);
 221                         break;
 222                 case 6:
 223                         ++fnd_flag;
 224                         strcpy (find_arg.entry.pool_uuid, optarg);
 225                         break;
 226                 case 7:
 227                         ++fnd_flag;
 228                         strcpy (find_arg.entry.dataset, optarg);
 229                         break;
 230                 case 8:
 231                         ++fnd_flag;
 232                         strcpy (find_arg.entry.kernel, optarg);
 233                         break;
 234                 case 9:
 235                         ++fnd_flag;
 236                         find_arg.entry.modules[(find_arg.entry.modules_amount)++] = strdup (optarg);
 237                         break;
 238                 case 10:
 239                         ++set_flag;
 240                         strcpy (set_arg.entry.entry_name, optarg);
 241                         break;
 242                 case 11:
 243                         ++set_flag;
 244                         strcpy (set_arg.entry.pool_label, optarg);
 245                         break;
 246                 case 12:
 247                         ++set_flag;
 248                         strcpy (set_arg.entry.pool_uuid, optarg);
 249                         break;
 250                 case 13:
 251                         ++set_flag;
 252                         strcpy (set_arg.entry.dataset, optarg);
 253                         break;
 254                 case 14:
 255                         ++set_flag;
 256                         strcpy (set_arg.entry.kernel, optarg);
 257                         break;
 258                 case 15:
 259                         ++set_flag;
 260                         strcpy (set_arg.entry.kernel_opts, optarg);
 261                         break;
 262                 case 16:
 263                         ++set_flag;
 264                         set_arg.entry.modules[(set_arg.entry.modules_amount)++] = strdup (optarg);
 265                         break;
 266                 case 17:
 267                         set_default = strdup (optarg);
 268                         break;
 269                 case 18:
 270                         set_timeout = strdup (optarg);
 271                         break;
 272                 case 19:
 273                         ++get_flag;
 274                         get_arg.entry.entry_name[0] = 1;
 275                         break;
 276                 case 20:
 277                         ++get_flag;
 278                         get_arg.entry.pool_label[0] = 1;
 279                         break;
 280                 case 21:
 281                         ++get_flag;
 282                         get_arg.entry.pool_uuid[0] = 1;
 283                         break;
 284                 case 22:
 285                         ++get_flag;
 286                         get_arg.entry.dataset[0] = 1;
 287                         break;
 288                 case 23:
 289                         ++get_flag;
 290                         get_arg.entry.kernel[0] = 1;
 291                         break;
 292                 case 24:
 293                         ++get_flag;
 294                         get_arg.entry.kernel_opts[0] = 1;
 295                         break;
 296                 case 25:
 297                         ++get_flag;
 298                         get_arg.entry.modules = (char**) 1;
 299                         break;
 300                 case 26:
 301                         ++get_default;
 302                         break;
 303                 case 27:
 304                         ++get_timeout;
 305                         break;
 306                 case 28:
 307                         ++get_flag;
 308                         memset (&(get_arg.entry), 0xFF, sizeof (menu_entry));
 309                         break;
 310                 case 'R':
 311                         alt_root = strdup (optarg);
 312                         break;
 313                 case 30:
 314                         ++set_flag;
 315                         strcpy (set_arg.entry.entry_name, "Menuadm default");
 316                         strcpy (set_arg.entry.pool_label, get_pool ("/"));
 317                         strcpy (set_arg.entry.kernel, "/platform/i86pc/kernel/$ISADIR/unix");
 318                         strcpy (set_arg.entry.kernel_opts, "-B $ZFS_BOOTFS");
 319                         set_arg.entry.modules[0] = strdup ("/platform/i86pc/$ISADIR/boot_archive");
 320                         set_arg.entry.modules_amount = 1;
 321                         break;
 322                 case 31:
 323                         set_serial = strdup (optarg);
 324                         break;
 325                 case 32:
 326                         set_terminal = strdup (optarg);
 327                         break;
 328                 case 33:
 329                         ++get_serial;
 330                         break;
 331                 case 34:
 332                         ++get_terminal;
 333                         break;
 334                 case 35:
 335                         ++cln_flag;
 336                         break;
 337                 case 36:
 338                         ++enh_flag;
 339                         break;
 340                 case 37:
 341                         ++dih_flag;
 342                         break;
 343                 case 38:
 344                         ++clr_flag;
 345                         break;
 346                 case 39:
 347                         ++num_flag;
 348                         sscanf (optarg, "%d", &number);
 349                         break;
 350                 default:
 351                         return 0;
 352                 }
 353         }
 354         return 1;
 355 }
 356 
 357 static int
 358 check_flags ()
 359 {
 360         const char fnc[] = "check_flags";
 361 
 362         debug_print ("%s\n", fnc);
 363 
 364         if (new_flag)
 365                 if (del_flag || all_flag || cln_flag || fnd_flag || num_flag)
 366                         return 0;
 367 
 368         if (del_flag)
 369                 if ((! fnd_flag && ! num_flag) || set_flag || get_flag || cln_flag || enh_flag || dih_flag)
 370                         return 0;
 371 
 372         if (cln_flag)
 373                 if (! fnd_flag || all_flag)
 374                         return 0; 
 375 
 376         if (fnd_flag)
 377                 if (num_flag)
 378                         return 0;
 379         return 1;
 380 }
 381 
 382 static void
 383 apply_set (menu_list * entry, menu_list * set)
 384 {
 385         unsigned int i = 0;
 386 
 387         const char fnc[] = "apply_set";
 388 
 389         debug_print ("%s\n", fnc);
 390 
 391         if (set->entry.entry_name[0])
 392                 strcpy (entry->entry.entry_name, set->entry.entry_name);
 393 
 394         if (set->entry.pool_label[0])
 395                 strcpy (entry->entry.pool_label, set->entry.pool_label);
 396 
 397         if (set->entry.pool_uuid[0])
 398                 strcpy (entry->entry.pool_uuid, set->entry.pool_uuid);
 399 
 400         if (set->entry.dataset[0])
 401                 strcpy (entry->entry.dataset, set->entry.dataset);
 402 
 403         if (set->entry.kernel[0])
 404                 strcpy (entry->entry.kernel, set->entry.kernel);
 405 
 406         if (set->entry.kernel_opts[0])
 407                 strcpy (entry->entry.kernel_opts, set->entry.kernel_opts);
 408 
 409         if (set->entry.modules_amount) {
 410                 for (i = 0; i < entry->entry.modules_amount; ++i)
 411                         free (entry->entry.modules[i]);
 412 
 413                 for (i = 0; i < set->entry.modules_amount; ++i)
 414                         entry->entry.modules[i] = strdup (set->entry.modules[i]);
 415 
 416                 entry->entry.modules_amount = set->entry.modules_amount;
 417         }
 418 }
 419 
 420 static void
 421 output_entry (menu_list * entry, menu_list * get)
 422 {
 423         unsigned int i = 0;
 424 
 425         const char fnc[] = "output_entry";
 426 
 427         debug_print ("%s\n", fnc);
 428 
 429         if (get->entry.entry_name[0])
 430                 printf("%s\n", entry->entry.entry_name);
 431 
 432         if (get->entry.pool_label[0])
 433                 printf("%s\n", entry->entry.pool_label);
 434 
 435         if (get->entry.pool_uuid[0])
 436                 printf("%s\n", entry->entry.pool_uuid);
 437 
 438         if (get->entry.dataset[0])
 439                 printf("%s\n", entry->entry.dataset);
 440 
 441         if (get->entry.kernel[0])
 442                 printf("%s\n", entry->entry.kernel);
 443 
 444         if (get->entry.kernel_opts[0])
 445                 printf("%s\n", entry->entry.kernel_opts);
 446 
 447         if (get->entry.modules)
 448                 for (i = 0; i < entry->entry.modules_amount; ++i)
 449                         printf("%s\n", entry->entry.modules[i]);
 450 
 451         printf("-----------------------------------------\n");
 452 }
 453 
 454 int
 455 main (int argc, char ** argv)
 456 {
 457         char * pool;
 458         char config[MAX_STRING_SIZE];
 459         char tmp_config[MAX_STRING_SIZE];
 460         menu_list * menu;
 461         menu_list * tmp;
 462         menu_list * entry;
 463         int entries_amount;
 464         int d_num;
 465 
 466         check_debug ();
 467 
 468         if (argc < 2)
 469                 ++lst_flag;
 470 
 471         set_arg.entry.modules = (char **) calloc (MAX_MODULE_AMOUNT, sizeof (char *));
 472         find_arg.entry.modules = (char **) calloc (MAX_MODULE_AMOUNT, sizeof (char *));
 473         if (! parse_args (argc, argv))
 474                 return 1;
 475 
 476         if (hlp_flag) {
 477                 usage ();
 478                 return 0;
 479         }
 480 
 481         if (! check_flags ()) {
 482                 print_error ("wrong option set, check \"man grubadm\"");
 483                 return 1;
 484         }
 485 
 486         pool = get_pool (alt_root ?: "/");
 487         strcpy (config, "/");
 488         if (alt_root)
 489                 strcat (config, alt_root);
 490         strcat (config, "/");
 491         strcat (config, pool);
 492         strcat (config, "/");
 493         strcpy (tmp_config, config);
 494         strcat (config, CONFIG_PATH);
 495         strcat (tmp_config, TMP_CONFIG_PATH);
 496 
 497         menu = parse_file (config, &entries_amount);
 498         if (entries_amount == -1) {
 499                 return 1;
 500         }
 501 
 502         tmp = menu;
 503         entry = NULL;
 504 
 505         do {
 506                 if (clr_flag) {
 507                         menu = NULL;
 508                         break;
 509                 }
 510 
 511                 if (set_default) {
 512                         sscanf (set_default, "%d", &d_num);
 513                         if (d_num < entries_amount)
 514                                 strcpy (default_entry, set_default);
 515                 }
 516         
 517                 if (set_timeout)
 518                         strcpy (timeout, set_timeout);
 519 
 520                 if (set_serial)
 521                         strcpy (serial, set_serial);
 522 
 523                 if (set_terminal)
 524                         strcpy (terminal, set_terminal);
 525 
 526                 if (fnd_flag) { 
 527                         entry = find_entry (tmp, &find_arg);
 528                         if (! entry) {
 529                                 print_error (MSG_ERR_NO_ENTRY);
 530                                 break;
 531                         }
 532                 }
 533 
 534                 if (num_flag) {
 535                         if ((number < 0) || (number >= entries_amount)) {
 536                                 if (! default_entry[0]) {
 537                                         number = 0;
 538                                 } else {
 539                                         sscanf (default_entry, "%d", &number);
 540                                 }
 541                         }
 542                         if (number >= entries_amount)
 543                                 number = 0;
 544                         entry = get_entry_by_number (menu, number);
 545                         if (! entry) {
 546                                 print_error (MSG_ERR_NO_ENTRY);
 547                                 break;
 548                         }
 549                 }
 550 
 551                 if (cln_flag) {
 552                         entry = clone_entry (entry);
 553                         if (! entry) {
 554                                 print_error (MSG_ERR_NO_ENTRY);
 555                                 break;
 556                         }
 557                 }
 558 
 559                 if (set_flag) {
 560                         if (entry)
 561                                 apply_set (entry, &set_arg);
 562                         else if (new_flag)
 563                                 add_entry (&menu, &set_arg);
 564                         else
 565                                 return 1;
 566                 }
 567 
 568                 if (enh_flag)
 569                         if (! enable_hyper (entry)) {
 570                                 print_error (MSG_ERR_HYPER);
 571                                 break;
 572                         }
 573                 
 574                 if (dih_flag)
 575                         if (! disable_hyper (entry)) {
 576                                 print_error (MSG_ERR_HYPER);
 577                                 break;
 578                         }
 579 
 580                 if (cln_flag)
 581                         add_entry (&menu, entry);
 582 
 583                 if (get_flag)
 584                         if (entry)
 585                                 output_entry (entry, &get_arg);
 586 
 587                 if (all_flag)
 588                         tmp = entry->next;
 589                 else
 590                         tmp = NULL;
 591 
 592                 if (del_flag)
 593                         delete_entry (&menu, entry);
 594         } while (tmp);
 595 
 596         if (get_default) 
 597                 printf ("%s%s%s\n", cmd_list[CMD_DEFAULT], SEPARATOR, default_entry);
 598 
 599         if (get_timeout)
 600                 printf ("%s%s%s\n", cmd_list[CMD_TIMEOUT], SEPARATOR, timeout);
 601 
 602         if (get_serial)
 603                 printf ("%s%s%s\n", cmd_list[CMD_SERIAL], SEPARATOR, serial);
 604 
 605         if (get_terminal)
 606                 printf ("%s%s%s\n", cmd_list[CMD_TERMINAL], SEPARATOR, terminal);
 607 
 608         if (lst_flag) {
 609                 list_menu (menu);
 610         }       
 611 
 612         write_menu (config, tmp_config, menu);
 613         free_menu (&menu);
 614         return 0;
 615 }