58d4dadf6ee4ec392a09d433802803520704cd7b
[grub.git] / grub-core / commands / menuentry.c
1 /* menuentry.c - menuentry command */
2 /*
3  *  GRUB  --  GRand Unified Bootloader
4  *  Copyright (C) 2010  Free Software Foundation, Inc.
5  *
6  *  GRUB is free software: you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation, either version 3 of the License, or
9  *  (at your option) any later version.
10  *
11  *  GRUB is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include <grub/types.h>
21 #include <grub/misc.h>
22 #include <grub/err.h>
23 #include <grub/dl.h>
24 #include <grub/extcmd.h>
25 #include <grub/i18n.h>
26 #include <grub/normal.h>
27
28 static const struct grub_arg_option options[] =
29   {
30     {"class", 1, GRUB_ARG_OPTION_REPEATABLE,
31      N_("Menu entry type."), N_("STRING"), ARG_TYPE_STRING},
32     {"users", 2, 0,
33      N_("List of users allowed to boot this entry."), N_("USERNAME[,USERNAME]"),
34      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     /* TRANSLATORS: menu entry can either be bootable by anyone or only by
41        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     {"unrestricted", 0, 0, N_("This entry can be booted by any user."),
45      0, ARG_TYPE_NONE},
46     {0, 0, 0, 0, 0, 0}
47   };
48
49 static struct
50 {
51   const char *name;
52   int key;
53 } hotkey_aliases[] =
54   {
55     {"backspace", '\b'},
56     {"tab", '\t'},
57     {"delete", GRUB_TERM_KEY_DC},
58     {"insert", GRUB_TERM_KEY_INSERT},
59     {"f1", GRUB_TERM_KEY_F1},
60     {"f2", GRUB_TERM_KEY_F2},
61     {"f3", GRUB_TERM_KEY_F3},
62     {"f4", GRUB_TERM_KEY_F4},
63     {"f5", GRUB_TERM_KEY_F5},
64     {"f6", GRUB_TERM_KEY_F6},
65     {"f7", GRUB_TERM_KEY_F7},
66     {"f8", GRUB_TERM_KEY_F8},
67     {"f9", GRUB_TERM_KEY_F9},
68     {"f10", GRUB_TERM_KEY_F10},
69     {"f11", GRUB_TERM_KEY_F11},
70     {"f12", GRUB_TERM_KEY_F12},
71   };
72
73 /* Add a menu entry to the current menu context (as given by the environment
74    variable data slot `menu').  As the configuration file is read, the script
75    parser calls this when a menu entry is to be created.  */
76 grub_err_t
77 grub_normal_add_menu_entry (int argc, const char **args,
78                             char **classes, const char *id,
79                             const char *users, const char *hotkey,
80                             const char *prefix, const char *sourcecode,
81                             int submenu)
82 {
83   int menu_hotkey = 0;
84   char **menu_args = NULL;
85   char *menu_users = NULL;
86   char *menu_title = NULL;
87   char *menu_sourcecode = NULL;
88   char *menu_id = NULL;
89   struct grub_menu_entry_class *menu_classes = NULL;
90
91   grub_menu_t menu;
92   grub_menu_entry_t *last;
93
94   menu = grub_env_get_menu ();
95   if (! menu)
96     return grub_error (GRUB_ERR_MENU, "no menu context");
97
98   last = &menu->entry_list;
99
100   menu_sourcecode = grub_xasprintf ("%s%s", prefix ?: "", sourcecode);
101   if (! menu_sourcecode)
102     return grub_errno;
103
104   if (classes && classes[0])
105     {
106       int i;
107       for (i = 0; classes[i]; i++); /* count # of menuentry classes */
108       menu_classes = grub_zalloc (sizeof (struct grub_menu_entry_class)
109                                   * (i + 1));
110       if (! menu_classes)
111         goto fail;
112
113       for (i = 0; classes[i]; i++)
114         {
115           menu_classes[i].name = grub_strdup (classes[i]);
116           if (! menu_classes[i].name)
117             goto fail;
118           menu_classes[i].next = classes[i + 1] ? &menu_classes[i + 1] : NULL;
119         }
120     }
121
122   if (users)
123     {
124       menu_users = grub_strdup (users);
125       if (! menu_users)
126         goto fail;
127     }
128
129   if (hotkey)
130     {
131       unsigned i;
132       for (i = 0; i < ARRAY_SIZE (hotkey_aliases); i++)
133         if (grub_strcmp (hotkey, hotkey_aliases[i].name) == 0)
134           {
135             menu_hotkey = hotkey_aliases[i].key;
136             break;
137           }
138       if (i == ARRAY_SIZE (hotkey_aliases))
139         menu_hotkey = hotkey[0];
140     }
141
142   if (! argc)
143     {
144       grub_error (GRUB_ERR_MENU, "menuentry is missing title");
145       goto fail;
146     }
147
148   menu_title = grub_strdup (args[0]);
149   if (! menu_title)
150     goto fail;
151
152   menu_id = grub_strdup (id ? : menu_title);
153   if (! menu_id)
154     goto fail;
155
156   /* Save argc, args to pass as parameters to block arg later. */
157   menu_args = grub_malloc (sizeof (char*) * (argc + 1));
158   if (! menu_args)
159     goto fail;
160
161   {
162     int i;
163     for (i = 0; i < argc; i++)
164       {
165         menu_args[i] = grub_strdup (args[i]);
166         if (! menu_args[i])
167           goto fail;
168       }
169     menu_args[argc] = NULL;
170   }
171
172   /* Add the menu entry at the end of the list.  */
173   while (*last)
174     last = &(*last)->next;
175
176   *last = grub_zalloc (sizeof (**last));
177   if (! *last)
178     goto fail;
179
180   (*last)->title = menu_title;
181   (*last)->id = menu_id;
182   (*last)->hotkey = menu_hotkey;
183   (*last)->classes = menu_classes;
184   if (menu_users)
185     (*last)->restricted = 1;
186   (*last)->users = menu_users;
187   (*last)->argc = argc;
188   (*last)->args = menu_args;
189   (*last)->sourcecode = menu_sourcecode;
190   (*last)->submenu = submenu;
191
192   menu->size++;
193   return GRUB_ERR_NONE;
194
195  fail:
196
197   grub_free (menu_sourcecode);
198   {
199     int i;
200     for (i = 0; menu_classes && menu_classes[i].name; i++)
201       grub_free (menu_classes[i].name);
202     grub_free (menu_classes);
203   }
204
205   {
206     int i;
207     for (i = 0; menu_args && menu_args[i]; i++)
208       grub_free (menu_args[i]);
209     grub_free (menu_args);
210   }
211
212   grub_free (menu_users);
213   grub_free (menu_title);
214   grub_free (menu_id);
215   return grub_errno;
216 }
217
218 static char *
219 setparams_prefix (int argc, char **args)
220 {
221   int i;
222   int j;
223   char *p;
224   char *result;
225   grub_size_t len = 10;
226
227   /* Count resulting string length */
228   for (i = 0; i < argc; i++)
229     {
230       len += 3; /* 3 = 1 space + 2 quotes */
231       p = args[i];
232       while (*p)
233         len += (*p++ == '\'' ? 3 : 1);
234     }
235
236   result = grub_malloc (len + 2);
237   if (! result)
238     return 0;
239
240   grub_strcpy (result, "setparams");
241   p = result + 9;
242
243   for (j = 0; j < argc; j++)
244     {
245       *p++ = ' ';
246       *p++ = '\'';
247       p = grub_strchrsub (p, args[j], '\'', "'\\''");
248       *p++ = '\'';
249     }
250   *p++ = '\n';
251   *p = '\0';
252   return result;
253 }
254
255 static grub_err_t
256 grub_cmd_menuentry (grub_extcmd_context_t ctxt, int argc, char **args)
257 {
258   char ch;
259   char *src;
260   char *prefix;
261   unsigned len;
262   grub_err_t r;
263   const char *users;
264
265   if (! argc)
266     return grub_error (GRUB_ERR_BAD_ARGUMENT, "missing arguments");
267
268   if (ctxt->state[3].set && ctxt->script)
269     return grub_error (GRUB_ERR_BAD_ARGUMENT, "multiple menuentry definitions");
270
271   if (! ctxt->state[3].set && ! ctxt->script)
272     return grub_error (GRUB_ERR_BAD_ARGUMENT, "no menuentry definition");
273
274   if (ctxt->state[1].set)
275     users = ctxt->state[1].arg;
276   else if (ctxt->state[5].set)
277     users = NULL;
278   else
279     users = "";
280
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                                         : NULL),
285                                        ctxt->state[4].arg,
286                                        users,
287                                        ctxt->state[2].arg, 0,
288                                        ctxt->state[3].arg,
289                                        ctxt->extcmd->cmd->name[0] == 's');
290
291   src = args[argc - 1];
292   args[argc - 1] = NULL;
293
294   len = grub_strlen(src);
295   ch = src[len - 1];
296   src[len - 1] = '\0';
297
298   prefix = setparams_prefix (argc - 1, args);
299   if (! prefix)
300     return grub_errno;
301
302   r = grub_normal_add_menu_entry (argc - 1, (const char **) args,
303                                   ctxt->state[0].args, ctxt->state[4].arg,
304                                   users,
305                                   ctxt->state[2].arg, prefix, src + 1,
306                                   ctxt->extcmd->cmd->name[0] == 's');
307
308   src[len - 1] = ch;
309   args[argc - 1] = src;
310   grub_free (prefix);
311   return r;
312 }
313
314 static grub_extcmd_t cmd, cmd_sub;
315
316 void
317 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   cmd_sub = grub_register_extcmd ("submenu", grub_cmd_menuentry,
325                                   GRUB_COMMAND_FLAG_BLOCKS
326                                   | GRUB_COMMAND_ACCEPT_DASH
327                                   | GRUB_COMMAND_FLAG_EXTRACTOR,
328                                   N_("BLOCK"), N_("Define a submenu."),
329                                   options);
330 }
331
332 void
333 grub_menu_fini (void)
334 {
335   grub_unregister_extcmd (cmd);
336   grub_unregister_extcmd (cmd_sub);
337 }