443756d87131dcf03eb8c257ddd309d133564d3f
[platform/core/system/deviced.git] / src / power-command / command.c
1 /*
2  * deviced
3  *
4  * Copyright (c) 2020 Samsung Electronics Co., Ltd.
5  *
6  * Licensed under the Apache License, Version 2.0 (the License);
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18
19 #include <getopt.h>
20 #include <libsyscommon/dbus-system.h>
21 #include <stdio.h>
22 #include <stdbool.h>
23 #include <core/common.h>
24 #include <stdlib.h>
25
26 enum application {
27         APP_UNKNOWN,
28         APP_SHUTDOWN,
29         APP_REBOOT,
30         APP_HALT,
31         APP_POWEROFF,
32 };
33
34 struct {
35         const char *name;
36         enum application application;
37 } apps[] = {
38         { .name = "reboot",   .application = APP_REBOOT,   },
39         { .name = "halt",     .application = APP_HALT,     },
40         { .name = "poweroff", .application = APP_POWEROFF, },
41         { .name = "shutdown", .application = APP_SHUTDOWN, },
42 };
43
44 enum application parse_path(const char *name)
45 {
46         const char *base = strrchr(name, '/');
47         base = base ? base + 1 : name;
48         for (size_t i = 0; i < ARRAY_SIZE(apps); ++i) {
49                 const char *target = apps[i].name;
50
51                 /* NB: We only check if the prefix matches,
52                  * so that we can run "reboot" as, for example "reboot-custom". */
53                 if (strncmp(base, target, strlen(target)) == 0)
54                         return apps[i].application;
55         }
56         return APP_UNKNOWN;
57 }
58
59 /* NB: we are emulating different programs: the "shutdown" program, and the trio of "reboot", "halt"
60  * and "poweroff". These two cases have different option sets, so they need separate parsing methods
61  * and separate usage strings. */
62
63 struct parse_result {
64         enum {
65                 DO_NOTHING,
66                 SHOW_SHUTDOWN_PARSE_ERROR,
67                 SHOW_REBOOT_ET_AL_PARSE_ERROR,
68                 SHOW_SHUTDOWN_HELP,
69                 SHOW_REBOOT_ET_AL_HELP,
70                 DO_POWEROFF,
71                 DO_EXIT,
72                 DO_REBOOT,
73         } operation;
74
75         const char *extra_option;
76
77         bool show_sync_warning;
78         bool show_timings_warning;
79         bool show_wall_warning;
80         bool show_wtmp_warning;
81 };
82
83 struct parse_result parse_shutdown(int argc, char **argv) {
84         enum {
85                 ACT_HALT,
86                 ACT_REBOOT,
87                 ACT_POWEROFF,
88         } action = ACT_POWEROFF;
89         bool will_do_nothing = false;
90         bool show_timings_warning = false;
91         bool show_wall_warning = false;
92
93         for (;;) {
94                 switch (getopt_long(argc, argv, "HPrhkc", (struct option []) {
95                         { "help",     no_argument, NULL, 0,   },
96                         { "no-wall",  no_argument, NULL, 1,   },
97                         { "halt",     no_argument, NULL, 'H', },
98                         { "poweroff", no_argument, NULL, 'P', },
99                         { "reboot",   no_argument, NULL, 'r', },
100                         { NULL,       0,           NULL, 0,   },
101                 }, NULL)) {
102                 case -1:
103                         switch (argc - optind) {
104                                 case 2:
105                                         show_wall_warning = true;
106                                         show_timings_warning = true;
107                                         break;
108
109                                 case 1:
110                                         show_timings_warning = true;
111                                         break;
112
113                                 case 0:
114                                         break;
115
116                                 default:
117                                         return (struct parse_result) { .operation = SHOW_SHUTDOWN_PARSE_ERROR, };
118                         }
119
120                         return (struct parse_result) {
121                                 .operation = will_do_nothing ? DO_NOTHING
122                                         : action == ACT_REBOOT ? DO_REBOOT
123                                         : action == ACT_HALT   ? DO_EXIT // don't ask difficult questions like "why not DO_HALT"
124                                         :                        DO_POWEROFF,
125                                 .extra_option = NULL,
126                                 .show_sync_warning = false,
127                                 .show_timings_warning = show_timings_warning,
128                                 .show_wall_warning = show_wall_warning,
129                                 .show_wtmp_warning = false,
130                         };
131
132                 case 0:
133                         return (struct parse_result) { .operation = SHOW_SHUTDOWN_HELP, };
134
135                 case 1:
136                         show_wall_warning = true;
137                         break;
138
139                 case 'H':
140                         action = ACT_HALT;
141                         break;
142
143                 case 'P':
144                         action = ACT_POWEROFF;
145                         break;
146
147                 case 'r':
148                         action = ACT_REBOOT;
149                         break;
150
151                 case 'h':
152                         if (action == ACT_REBOOT)
153                                 action = ACT_POWEROFF;
154                         break;
155
156                 case 'k':
157                         will_do_nothing = true;
158                         show_wall_warning = true;
159                         break;
160
161                 case 'c':
162                         will_do_nothing = true;
163                         show_timings_warning = true;
164                         break;
165
166                 case '?':
167                         return (struct parse_result) { .operation = SHOW_SHUTDOWN_PARSE_ERROR, };
168
169                 default:
170                         assert(false);
171                 }
172         }
173 }
174
175 struct parse_result parse_reboot_et_al(enum application application, int argc, char **argv) {
176         enum {
177                 ACT_HALT,
178                 ACT_REBOOT,
179                 ACT_POWEROFF,
180         } action;
181         switch (application) {
182                 case APP_REBOOT:
183                         action = ACT_REBOOT;
184                         break;
185
186                 case APP_HALT:
187                         action = ACT_HALT;
188                         break;
189
190                 case APP_POWEROFF:
191                         action = ACT_POWEROFF;
192                         break;
193
194                 default:
195                         assert(false);
196         }
197
198         bool will_do_nothing = false;
199         bool show_sync_warning = false;
200         bool show_timings_warning = false;
201         bool show_wall_warning = false;
202         bool show_wtmp_warning = false;
203         char *extra_option = NULL;
204
205         for (;;) {
206                 switch (getopt_long(argc, argv, "-pfwdn", (struct option []) {
207                         { "help",      no_argument, NULL, 0,   },
208                         /* 1 reserved for options not in -foo format */
209                         { "halt",      no_argument, NULL, 2,   },
210                         { "reboot",    no_argument, NULL, 3,   },
211                         { "no-wall",   no_argument, NULL, 4,   },
212                         { "poweroff",  no_argument, NULL, 'p', },
213                         { "force",     no_argument, NULL, 'f', },
214                         { "wtmp-only", no_argument, NULL, 'w', },
215                         { "no-wtmp",   no_argument, NULL, 'd', },
216                         { "no-sync",   no_argument, NULL, 'n', },
217                         { NULL,        0,           NULL, 0,   },
218                 }, NULL)) {
219                 case -1:
220                         if (optind != argc)
221                                 return (struct parse_result) { .operation = SHOW_REBOOT_ET_AL_PARSE_ERROR, };
222
223                         return (struct parse_result) {
224                                 .operation = will_do_nothing ? DO_NOTHING
225                                         : action == ACT_REBOOT ? DO_REBOOT
226                                         : action == ACT_HALT   ? DO_EXIT
227                                         :                        DO_POWEROFF,
228                                 .extra_option = extra_option,
229                                 .show_sync_warning = show_sync_warning,
230                                 .show_timings_warning = show_timings_warning,
231                                 .show_wall_warning = show_wall_warning,
232                                 .show_wtmp_warning = show_wtmp_warning,
233                         };
234
235                 case 0:
236                         return (struct parse_result) { .operation = SHOW_REBOOT_ET_AL_HELP, };
237
238                 case 1:
239                         if (extra_option)
240                                 return (struct parse_result) { .operation = SHOW_REBOOT_ET_AL_PARSE_ERROR, };
241                         extra_option = optarg;
242                         break;
243
244                 case 2:
245                         action = ACT_HALT;
246                         break;
247
248                 case 3:
249                         action = ACT_REBOOT;
250                         break;
251
252                 case 4:
253                         show_wall_warning = true;
254                         break;
255
256                 case 'p':
257                         action = ACT_POWEROFF;
258                         break;
259
260                 case 'f':
261                         show_timings_warning = false;
262                         break;
263
264                 case 'w':
265                         will_do_nothing = true;
266                         show_wtmp_warning = true;
267                         break;
268
269                 case 'd':
270                         show_wtmp_warning = true;
271                         break;
272
273                 case 'n':
274                         show_sync_warning = true;
275                         break;
276
277                 case '?':
278                         return (struct parse_result) { .operation = SHOW_REBOOT_ET_AL_PARSE_ERROR, };
279
280                 default:
281                         assert(false);
282                 }
283         }
284 }
285
286 int call_deviced_poweroff(const char *type, const char *extra_option, const char *message)
287 {
288         int result = extra_option
289                 ? dbus_handle_method_sync_var(DEVICED_BUS_NAME, DEVICED_PATH_POWEROFF, DEVICED_INTERFACE_POWEROFF, "PowerOffWithOption", g_variant_new("(ss)", type, extra_option), NULL)
290                 : dbus_handle_method_sync_var(DEVICED_BUS_NAME, DEVICED_PATH_POWEROFF, DEVICED_INTERFACE_POWEROFF, "PowerOff", g_variant_new("(s)", type), NULL)
291         ;
292         if (result < 0) {
293                 fprintf(stderr, "Error: %d", result);
294                 return EXIT_FAILURE;
295         }
296
297         fprintf(stderr, "%s", message);
298         for (;;)
299                 pause();
300         return EXIT_SUCCESS;
301 }
302
303 int main(int argc, char **argv)
304 {
305         enum application application = parse_path(argv[0]);
306         struct parse_result parse_result;
307         switch (application) {
308         case APP_UNKNOWN:
309                 fprintf(stderr, "This program can only be used as a symlink to 'reboot', 'halt', 'poweroff' or 'shutdown'.\n");
310                 return EXIT_FAILURE;
311
312         case APP_SHUTDOWN:
313                 parse_result = parse_shutdown(argc, argv);
314                 break;
315
316         case APP_REBOOT:
317         case APP_HALT:
318         case APP_POWEROFF:
319                 parse_result = parse_reboot_et_al(application, argc, argv);
320                 break;
321
322         default:
323                 // This shouldn't be needed, but GCC complains otherwise.
324                 assert(false);
325                 return EXIT_FAILURE;
326         }
327
328         if (parse_result.show_sync_warning)
329                 fprintf(stderr, "Warning: Tizen always syncs, sync options ignored.\n");
330         if (parse_result.show_timings_warning)
331                 fprintf(stderr, "Warning: %s doesn't take care of delayed and pending operations on Tizen.\n", argv[0]);
332         if (parse_result.show_wall_warning)
333                 fprintf(stderr, "Warning: %s doesn't take care of wall messages on Tizen.\n", argv[0]);
334         if (parse_result.show_wtmp_warning)
335                 fprintf(stderr, "Warning: %s doesn't take care of wtmp entries on Tizen.\n", argv[0]);
336
337         switch (parse_result.operation) {
338         case DO_NOTHING:
339                 return EXIT_SUCCESS;
340
341         case SHOW_SHUTDOWN_HELP:
342         case SHOW_SHUTDOWN_PARSE_ERROR:
343                 fprintf(stderr, "Usage: %s [-HPrh]\n", argv[0]);
344                 return parse_result.operation == SHOW_SHUTDOWN_HELP ? EXIT_SUCCESS : EXIT_FAILURE;
345
346         case SHOW_REBOOT_ET_AL_HELP:
347         case SHOW_REBOOT_ET_AL_PARSE_ERROR:
348                 fprintf(stderr, "Usage: %s [-p] [--halt|--poweroff|--reboot] [extra]\n", argv[0]); // FIXME: do we say something about the extras? if yes, do we mention deviced, the configs etc?
349                 return parse_result.operation == SHOW_REBOOT_ET_AL_HELP ? EXIT_SUCCESS : EXIT_FAILURE;
350
351         case DO_POWEROFF:
352                 return call_deviced_poweroff("poweroff", parse_result.extra_option, "Shutting down...\n"); // should be "Powering off" really, "shutting down" is the generic term also encompassing halt/reboot >_>
353         case DO_EXIT:
354                 return call_deviced_poweroff("exit", parse_result.extra_option, "Shutting down...\n"); // ditto: exit ultimately boils down to poweroff, too
355         case DO_REBOOT:
356                 return call_deviced_poweroff("reboot", parse_result.extra_option, "Rebooting...\n");
357         }
358
359         assert(false); // unreachable
360         return EXIT_FAILURE;
361 }