menuentry.c 8.33 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
/* menuentry.c - menuentry command */
/*
 *  GRUB  --  GRand Unified Bootloader
 *  Copyright (C) 2010  Free Software Foundation, Inc.
 *
 *  GRUB is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  GRUB is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <grub/types.h>
#include <grub/misc.h>
#include <grub/err.h>
#include <grub/dl.h>
#include <grub/extcmd.h>
#include <grub/i18n.h>
#include <grub/normal.h>

28 29 30
static const struct grub_arg_option options[] =
  {
    {"class", 1, GRUB_ARG_OPTION_REPEATABLE,
31
     N_("Menu entry type."), N_("STRING"), ARG_TYPE_STRING},
32
    {"users", 2, 0,
33 34
     N_("List of users allowed to boot this entry."), N_("USERNAME[,USERNAME]"),
     ARG_TYPE_STRING},
35
    {"hotkey", 3, 0,
36
     N_("Keyboard key to quickly boot this entry."), N_("KEYBOARD_KEY"), ARG_TYPE_STRING},
37
    {"source", 4, 0,
38
     N_("Use STRING as menu entry body."), N_("STRING"), ARG_TYPE_STRING},
39
    {"id", 0, 0, N_("Menu entry identifier."), N_("STRING"), ARG_TYPE_STRING},
40 41
    /* TRANSLATORS: menu entry can either be bootable by anyone or only by
       handful of users. By default when security is active only superusers can
42
       boot a given menu entry. With --unrestricted (this option)
43
       anyone can boot it.  */
44 45
    {"unrestricted", 0, 0, N_("This entry can be booted by any user."),
     0, ARG_TYPE_NONE},
46 47 48 49 50
    {0, 0, 0, 0, 0, 0}
  };

static struct
{
51
  const char *name;
52 53 54 55 56
  int key;
} hotkey_aliases[] =
  {
    {"backspace", '\b'},
    {"tab", '\t'},
57 58 59 60 61 62 63 64 65 66 67 68 69 70
    {"delete", GRUB_TERM_KEY_DC},
    {"insert", GRUB_TERM_KEY_INSERT},
    {"f1", GRUB_TERM_KEY_F1},
    {"f2", GRUB_TERM_KEY_F2},
    {"f3", GRUB_TERM_KEY_F3},
    {"f4", GRUB_TERM_KEY_F4},
    {"f5", GRUB_TERM_KEY_F5},
    {"f6", GRUB_TERM_KEY_F6},
    {"f7", GRUB_TERM_KEY_F7},
    {"f8", GRUB_TERM_KEY_F8},
    {"f9", GRUB_TERM_KEY_F9},
    {"f10", GRUB_TERM_KEY_F10},
    {"f11", GRUB_TERM_KEY_F11},
    {"f12", GRUB_TERM_KEY_F12},
71 72 73 74 75
  };

/* Add a menu entry to the current menu context (as given by the environment
   variable data slot `menu').  As the configuration file is read, the script
   parser calls this when a menu entry is to be created.  */
76
grub_err_t
77 78
grub_normal_add_menu_entry (int argc, const char **args,
			    char **classes, const char *id,
79
			    const char *users, const char *hotkey,
80 81
			    const char *prefix, const char *sourcecode,
			    int submenu)
82 83
{
  int menu_hotkey = 0;
84
  char **menu_args = NULL;
85 86 87
  char *menu_users = NULL;
  char *menu_title = NULL;
  char *menu_sourcecode = NULL;
88
  char *menu_id = NULL;
89 90 91 92 93 94 95 96 97 98 99
  struct grub_menu_entry_class *menu_classes = NULL;

  grub_menu_t menu;
  grub_menu_entry_t *last;

  menu = grub_env_get_menu ();
  if (! menu)
    return grub_error (GRUB_ERR_MENU, "no menu context");

  last = &menu->entry_list;

BVK Chaitanya's avatar
BVK Chaitanya committed
100
  menu_sourcecode = grub_xasprintf ("%s%s", prefix ?: "", sourcecode);
101 102 103
  if (! menu_sourcecode)
    return grub_errno;

104
  if (classes && classes[0])
105
    {
106
      int i;
107
      for (i = 0; classes[i]; i++); /* count # of menuentry classes */
108 109
      menu_classes = grub_zalloc (sizeof (struct grub_menu_entry_class)
				  * (i + 1));
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
      if (! menu_classes)
	goto fail;

      for (i = 0; classes[i]; i++)
	{
	  menu_classes[i].name = grub_strdup (classes[i]);
	  if (! menu_classes[i].name)
	    goto fail;
	  menu_classes[i].next = classes[i + 1] ? &menu_classes[i + 1] : NULL;
	}
    }

  if (users)
    {
      menu_users = grub_strdup (users);
      if (! menu_users)
	goto fail;
    }

  if (hotkey)
    {
131
      unsigned i;
132 133 134 135 136 137
      for (i = 0; i < ARRAY_SIZE (hotkey_aliases); i++)
	if (grub_strcmp (hotkey, hotkey_aliases[i].name) == 0)
	  {
	    menu_hotkey = hotkey_aliases[i].key;
	    break;
	  }
BVK Chaitanya's avatar
BVK Chaitanya committed
138 139
      if (i == ARRAY_SIZE (hotkey_aliases))
	menu_hotkey = hotkey[0];
140 141 142 143 144 145 146 147 148 149 150 151
    }

  if (! argc)
    {
      grub_error (GRUB_ERR_MENU, "menuentry is missing title");
      goto fail;
    }

  menu_title = grub_strdup (args[0]);
  if (! menu_title)
    goto fail;

152 153 154 155
  menu_id = grub_strdup (id ? : menu_title);
  if (! menu_id)
    goto fail;

156 157 158 159 160
  /* Save argc, args to pass as parameters to block arg later. */
  menu_args = grub_malloc (sizeof (char*) * (argc + 1));
  if (! menu_args)
    goto fail;

161 162 163 164 165 166 167 168 169 170
  {
    int i;
    for (i = 0; i < argc; i++)
      {
	menu_args[i] = grub_strdup (args[i]);
	if (! menu_args[i])
	  goto fail;
      }
    menu_args[argc] = NULL;
  }
171 172 173 174 175 176 177 178 179 180

  /* Add the menu entry at the end of the list.  */
  while (*last)
    last = &(*last)->next;

  *last = grub_zalloc (sizeof (**last));
  if (! *last)
    goto fail;

  (*last)->title = menu_title;
181
  (*last)->id = menu_id;
182 183 184 185 186
  (*last)->hotkey = menu_hotkey;
  (*last)->classes = menu_classes;
  if (menu_users)
    (*last)->restricted = 1;
  (*last)->users = menu_users;
187 188
  (*last)->argc = argc;
  (*last)->args = menu_args;
189
  (*last)->sourcecode = menu_sourcecode;
190
  (*last)->submenu = submenu;
191 192 193 194 195 196 197

  menu->size++;
  return GRUB_ERR_NONE;

 fail:

  grub_free (menu_sourcecode);
198 199 200 201 202 203
  {
    int i;
    for (i = 0; menu_classes && menu_classes[i].name; i++)
      grub_free (menu_classes[i].name);
    grub_free (menu_classes);
  }
204

205 206 207 208 209 210
  {
    int i;
    for (i = 0; menu_args && menu_args[i]; i++)
      grub_free (menu_args[i]);
    grub_free (menu_args);
  }
211

212 213
  grub_free (menu_users);
  grub_free (menu_title);
214
  grub_free (menu_id);
215 216 217
  return grub_errno;
}

BVK Chaitanya's avatar
BVK Chaitanya committed
218 219 220 221 222 223 224 225 226 227 228 229 230 231 232
static char *
setparams_prefix (int argc, char **args)
{
  int i;
  int j;
  char *p;
  char *result;
  grub_size_t len = 10;

  /* Count resulting string length */
  for (i = 0; i < argc; i++)
    {
      len += 3; /* 3 = 1 space + 2 quotes */
      p = args[i];
      while (*p)
233
	len += (*p++ == '\'' ? 3 : 1);
BVK Chaitanya's avatar
BVK Chaitanya committed
234 235 236 237 238 239 240
    }

  result = grub_malloc (len + 2);
  if (! result)
    return 0;

  grub_strcpy (result, "setparams");
241
  p = result + 9;
BVK Chaitanya's avatar
BVK Chaitanya committed
242 243 244

  for (j = 0; j < argc; j++)
    {
245 246
      *p++ = ' ';
      *p++ = '\'';
247
      p = grub_strchrsub (p, args[j], '\'', "'\\''");
248
      *p++ = '\'';
BVK Chaitanya's avatar
BVK Chaitanya committed
249
    }
250 251
  *p++ = '\n';
  *p = '\0';
BVK Chaitanya's avatar
BVK Chaitanya committed
252 253 254
  return result;
}

255 256 257
static grub_err_t
grub_cmd_menuentry (grub_extcmd_context_t ctxt, int argc, char **args)
{
258
  char ch;
259
  char *src;
BVK Chaitanya's avatar
BVK Chaitanya committed
260
  char *prefix;
261
  unsigned len;
262
  grub_err_t r;
263
  const char *users;
264

265 266 267 268 269 270 271 272 273
  if (! argc)
    return grub_error (GRUB_ERR_BAD_ARGUMENT, "missing arguments");

  if (ctxt->state[3].set && ctxt->script)
    return grub_error (GRUB_ERR_BAD_ARGUMENT, "multiple menuentry definitions");

  if (! ctxt->state[3].set && ! ctxt->script)
    return grub_error (GRUB_ERR_BAD_ARGUMENT, "no menuentry definition");

274 275 276 277 278 279 280
  if (ctxt->state[1].set)
    users = ctxt->state[1].arg;
  else if (ctxt->state[5].set)
    users = NULL;
  else
    users = "";

281
  if (! ctxt->script)
282
    return grub_normal_add_menu_entry (argc, (const char **) args,
283
				       (ctxt->state[0].set ? ctxt->state[0].args
284 285
					: NULL),
				       ctxt->state[4].arg,
286
				       users,
287
				       ctxt->state[2].arg, 0,
288 289
				       ctxt->state[3].arg,
				       ctxt->extcmd->cmd->name[0] == 's');
290 291

  src = args[argc - 1];
292 293
  args[argc - 1] = NULL;

294
  len = grub_strlen(src);
295
  ch = src[len - 1];
296
  src[len - 1] = '\0';
297

BVK Chaitanya's avatar
BVK Chaitanya committed
298 299 300 301
  prefix = setparams_prefix (argc - 1, args);
  if (! prefix)
    return grub_errno;

302
  r = grub_normal_add_menu_entry (argc - 1, (const char **) args,
303
				  ctxt->state[0].args, ctxt->state[4].arg,
304
				  users,
305 306
				  ctxt->state[2].arg, prefix, src + 1,
				  ctxt->extcmd->cmd->name[0] == 's');
307

308
  src[len - 1] = ch;
309
  args[argc - 1] = src;
BVK Chaitanya's avatar
BVK Chaitanya committed
310
  grub_free (prefix);
311 312 313
  return r;
}

314
static grub_extcmd_t cmd, cmd_sub;
315

316 317
void
grub_menu_init (void)
318 319
{
  cmd = grub_register_extcmd ("menuentry", grub_cmd_menuentry,
320
			      GRUB_COMMAND_FLAG_BLOCKS
321
			      | GRUB_COMMAND_ACCEPT_DASH
322
			      | GRUB_COMMAND_FLAG_EXTRACTOR,
323
			      N_("BLOCK"), N_("Define a menu entry."), options);
324 325
  cmd_sub = grub_register_extcmd ("submenu", grub_cmd_menuentry,
				  GRUB_COMMAND_FLAG_BLOCKS
326
				  | GRUB_COMMAND_ACCEPT_DASH
327 328 329
				  | GRUB_COMMAND_FLAG_EXTRACTOR,
				  N_("BLOCK"), N_("Define a submenu."),
				  options);
330 331
}

332 333
void
grub_menu_fini (void)
334 335
{
  grub_unregister_extcmd (cmd);
336
  grub_unregister_extcmd (cmd_sub);
337
}