1 // %NO_EDIT_WARNING%
2
3 ////////////////////////////////////////////////////////////////////////
4 //
5 // Copyright (C) 2012-2021 The Octave Project Developers
6 //
7 // See the file COPYRIGHT.md in the top-level directory of this
8 // distribution or <https://octave.org/copyright/>.
9 //
10 // This file is part of Octave.
11 //
12 // Octave is free software: you can redistribute it and/or modify it
13 // under the terms of the GNU General Public License as published by
14 // the Free Software Foundation, either version 3 of the License, or
15 // (at your option) any later version.
16 //
17 // Octave is distributed in the hope that it will be useful, but
18 // WITHOUT ANY WARRANTY; without even the implied warranty of
19 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 // GNU General Public License for more details.
21 //
22 // You should have received a copy of the GNU General Public License
23 // along with Octave; see the file COPYING. If not, see
24 // <https://www.gnu.org/licenses/>.
25 //
26 ////////////////////////////////////////////////////////////////////////
27
28 // NOTE: This program is supposed to be a small wrapper that exists
29 // primarily to give up the controlling TTY and then exec Octave with
30 // its GUI. It may also execute Octave without the GUI or the command
31 // line version of Octave that is not linked with GUI libraries. So
32 // that it remains small, it should NOT depend on or be linked with
33 // liboctave or libinterp.
34
35 #if defined (HAVE_CONFIG_H)
36 # include "config.h"
37 #endif
38
39 #include <cstdlib>
40 #include <cstring>
41
42 #include <algorithm>
43 #include <iostream>
44 #include <string>
45
46 #include "fcntl-wrappers.h"
47 #include "getopt-wrapper.h"
48 #include "signal-wrappers.h"
49 #include "unistd-wrappers.h"
50 #include "wait-wrappers.h"
51
52 #if ! defined (OCTAVE_VERSION)
53 # define OCTAVE_VERSION %OCTAVE_VERSION%
54 #endif
55
56 #if ! defined (OCTAVE_ARCHLIBDIR)
57 # define OCTAVE_ARCHLIBDIR %OCTAVE_ARCHLIBDIR%
58 #endif
59
60 #if ! defined (OCTAVE_BINDIR)
61 # define OCTAVE_BINDIR %OCTAVE_BINDIR%
62 #endif
63
64 #if ! defined (OCTAVE_PREFIX)
65 # define OCTAVE_PREFIX %OCTAVE_PREFIX%
66 #endif
67
68 #if ! defined (OCTAVE_EXEC_PREFIX)
69 # define OCTAVE_EXEC_PREFIX %OCTAVE_EXEC_PREFIX%
70 #endif
71
72 #include "display-available.h"
73 #include "options.h"
74 #include "shared-fcns.h"
75
76 #if defined (HAVE_OCTAVE_QT_GUI) && ! defined (OCTAVE_USE_WINDOWS_API)
77
78 // Forward signals to the GUI process.
79
80 static pid_t gui_pid = 0;
81
82 static int caught_signal = -1;
83
84 static void
gui_driver_sig_handler(int sig)85 gui_driver_sig_handler (int sig)
86 {
87 if (gui_pid > 0)
88 caught_signal = sig;
89 }
90
91 static void
gui_driver_set_signal_handler(const char * signame,octave_sig_handler * handler)92 gui_driver_set_signal_handler (const char *signame,
93 octave_sig_handler *handler)
94 {
95 octave_set_signal_handler_by_name (signame, handler, false);
96 }
97
98 static void
install_signal_handlers(void)99 install_signal_handlers (void)
100 {
101 // FIXME: do we need to handle and forward all the signals that Octave
102 // handles, or is it sufficient to only forward things like SIGINT,
103 // SIGBREAK, SIGABRT, SIGQUIT, and possibly a few others?
104
105 gui_driver_set_signal_handler ("SIGINT", gui_driver_sig_handler);
106 gui_driver_set_signal_handler ("SIGBREAK", gui_driver_sig_handler);
107 gui_driver_set_signal_handler ("SIGABRT", gui_driver_sig_handler);
108 gui_driver_set_signal_handler ("SIGALRM", gui_driver_sig_handler);
109 gui_driver_set_signal_handler ("SIGBUS", gui_driver_sig_handler);
110
111 // SIGCHLD
112 // SIGCLD
113 // SIGCONT
114
115 gui_driver_set_signal_handler ("SIGEMT", gui_driver_sig_handler);
116 gui_driver_set_signal_handler ("SIGFPE", gui_driver_sig_handler);
117 gui_driver_set_signal_handler ("SIGHUP", gui_driver_sig_handler);
118 gui_driver_set_signal_handler ("SIGILL", gui_driver_sig_handler);
119
120 // SIGINFO
121 // SIGINT
122
123 gui_driver_set_signal_handler ("SIGIOT", gui_driver_sig_handler);
124 gui_driver_set_signal_handler ("SIGLOST", gui_driver_sig_handler);
125 gui_driver_set_signal_handler ("SIGPIPE", gui_driver_sig_handler);
126 gui_driver_set_signal_handler ("SIGPOLL", gui_driver_sig_handler);
127
128 // SIGPROF
129 // SIGPWR
130
131 gui_driver_set_signal_handler ("SIGQUIT", gui_driver_sig_handler);
132 gui_driver_set_signal_handler ("SIGSEGV", gui_driver_sig_handler);
133
134 // SIGSTOP
135
136 gui_driver_set_signal_handler ("SIGSYS", gui_driver_sig_handler);
137 gui_driver_set_signal_handler ("SIGTERM", gui_driver_sig_handler);
138 gui_driver_set_signal_handler ("SIGTRAP", gui_driver_sig_handler);
139
140 // SIGTSTP
141 // SIGTTIN
142 // SIGTTOU
143 // SIGURG
144
145 gui_driver_set_signal_handler ("SIGUSR1", gui_driver_sig_handler);
146 gui_driver_set_signal_handler ("SIGUSR2", gui_driver_sig_handler);
147 gui_driver_set_signal_handler ("SIGVTALRM", gui_driver_sig_handler);
148 gui_driver_set_signal_handler ("SIGIO", gui_driver_sig_handler);
149
150 // SIGWINCH
151
152 gui_driver_set_signal_handler ("SIGXCPU", gui_driver_sig_handler);
153 gui_driver_set_signal_handler ("SIGXFSZ", gui_driver_sig_handler);
154 }
155
156 #endif
157
158 static std::string
get_octave_bindir(void)159 get_octave_bindir (void)
160 {
161 // Accept value from the environment literally, but substitute
162 // OCTAVE_HOME in the configuration value OCTAVE_BINDIR in case Octave
163 // has been relocated to some installation directory other than the
164 // one originally configured.
165
166 std::string obd = octave_getenv ("OCTAVE_BINDIR");
167
168 return obd.empty () ? prepend_octave_exec_home (std::string (OCTAVE_BINDIR))
169 : obd;
170 }
171
172 static std::string
get_octave_archlibdir(void)173 get_octave_archlibdir (void)
174 {
175 // Accept value from the environment literally, but substitute
176 // OCTAVE_HOME in the configuration value OCTAVE_ARCHLIBDIR in case
177 // Octave has been relocated to some installation directory other than
178 // the one originally configured.
179
180 std::string dir = octave_getenv ("OCTAVE_ARCHLIBDIR");
181
182 return dir.empty () ? prepend_octave_exec_home (std::string (OCTAVE_ARCHLIBDIR))
183 : dir;
184 }
185
186 static int
octave_exec(const std::string & file,char ** argv)187 octave_exec (const std::string& file, char **argv)
188 {
189 int status = octave_execv_wrapper (file.c_str (), argv);
190
191 std::cerr << argv[0] << ": failed to exec '" << file << "'" << std::endl;
192
193 return status;
194 }
195
196 static char *
strsave(const char * s)197 strsave (const char *s)
198 {
199 if (! s)
200 return nullptr;
201
202 int len = strlen (s);
203 char *tmp = new char [len+1];
204 tmp = strcpy (tmp, s);
205 return tmp;
206 }
207
208 int
main(int argc,char ** argv)209 main (int argc, char **argv)
210 {
211 int retval = 0;
212
213 int idx_gui = -1;
214 bool start_gui = false;
215 bool gui_libs = true;
216
217 bool eval_code = false;
218 bool persist_octave = false;
219
220 set_octave_home ();
221
222 std::string octave_bindir = get_octave_bindir ();
223 std::string octave_archlibdir = get_octave_archlibdir ();
224 std::string octave_cli
225 = octave_bindir + dir_sep_char + "octave-cli-" OCTAVE_VERSION;
226 std::string octave_gui = octave_archlibdir + dir_sep_char + "octave-gui";
227
228 #if defined (HAVE_OCTAVE_QT_GUI)
229 // The Octave version number is already embedded in the
230 // octave_archlibdir directory name so we don't need to append it to
231 // the octave-gui filename.
232
233 std::string file = octave_gui;
234 #else
235 std::string file = octave_cli;
236 #endif
237
238 // Declaring new_argv static avoids leak warnings when using GCC's
239 // --address-sanitizer option.
240 static char **new_argv = new char * [argc + 2];
241
242 int next_optind = 1;
243 int k = 1;
244
245 bool warn_display = true;
246 bool no_display = false;
247
248 // Disable error reporting in getopt. We want to silently recognize
249 // and process a few special arguments here and pass everything on to
250 // the real Octave program where incorrect usage errors may be
251 // reported.
252
253 octave_set_opterr_wrapper (0);
254
255 while (true)
256 {
257 int long_idx;
258
259 int optc = octave_getopt_long_wrapper (argc, argv, short_opts, long_opts,
260 &long_idx);
261 int old_optind = next_optind;
262 next_optind = octave_optind_wrapper ();
263
264 if (optc < 0)
265 break;
266
267 switch (optc)
268 {
269 case NO_GUI_LIBS_OPTION:
270 // Run the version of Octave that is not linked with any GUI
271 // libraries. It may not be possible to do plotting or any ui*
272 // calls, but it will be a little faster to start and require less
273 // memory. Don't pass the --no-gui-libs option on as that option
274 // is not recognized by Octave.
275 gui_libs = false;
276 file = octave_cli;
277 break;
278
279 case NO_GUI_OPTION:
280 // If we see this option, then we can just exec octave; we don't
281 // have to create a child process and wait for it to exit. But do
282 // exec "octave-gui", not "octave-cli", because even if the
283 // --no-gui option is given, we may be asked to do some plotting or
284 // ui* calls.
285 start_gui = false;
286 new_argv[k++] = argv[old_optind];
287 break;
288
289 case GUI_OPTION:
290 // If we see this option, then we fork and exec octave with the
291 // --gui option, while continuing to handle signals in the
292 // terminal.
293 // Do not copy the arg now, since we still not know if the gui
294 // should really be launched. Just store the index.
295 start_gui = true;
296 idx_gui = old_optind;
297 break;
298
299
300 case PERSIST_OPTION:
301 // FIXME: How can we reliably detect if this option appears after
302 // a FILE argument. In this case octave ignores the option,
303 // but the GUI might still be launched if --gui is also
304 // given.
305 persist_octave = true;
306 new_argv[k++] = argv[old_optind];
307 break;
308
309 case EVAL_OPTION:
310 eval_code = true;
311 for (int i = old_optind; i < next_optind; i++)
312 new_argv[k++] = argv[i];
313 break;
314
315 case 'q':
316 // options "--silent" or "--quiet"
317 warn_display = false;
318 new_argv[k++] = argv[old_optind];
319 break;
320
321 case 'W':
322 // option "--no-window-system"
323 no_display = true;
324 new_argv[k++] = argv[old_optind];
325 break;
326
327 default:
328 for (int i = old_optind; i < next_optind; i++)
329 new_argv[k++] = argv[i];
330 break;
331 }
332 }
333
334 // Treat trailing arguments as commands to be executed
335 if (next_optind < argc)
336 {
337 eval_code = true;
338 for (int i = next_optind; i < argc; i++)
339 new_argv[k++] = argv[i];
340 }
341
342 if (start_gui && eval_code && ! persist_octave)
343 start_gui = false;
344
345 // At this point, we definitely know whether the gui has to
346 // be launched or not.
347 // gui_libs and start_gui are just about options, not
348 // the environment. Exit if they don't make sense.
349 if (start_gui)
350 {
351 // GUI should be started
352 if (! gui_libs)
353 {
354 std::cerr << "octave: conflicting options: --no-gui-libs and --gui"
355 << std::endl;
356 return 1;
357 }
358
359 #if ! defined (HAVE_OCTAVE_QT_GUI)
360 std::cerr << "octave: GUI features missing or disabled in this build"
361 << std::endl;
362 return 1;
363 #endif
364
365 // Finally, add --gui to the command line options. We can not
366 // just append it since options after a given file are ignored.
367 for (int j = k; j > 1; j--)
368 new_argv[j] = new_argv[j-1];
369
370 new_argv[1] = argv[idx_gui];
371 k++;
372 }
373
374 new_argv[k] = nullptr;
375
376 if (no_display)
377 {
378 start_gui = false;
379 gui_libs = false;
380
381 file = octave_cli;
382 }
383 else if (gui_libs || start_gui)
384 {
385 int dpy_avail;
386
387 const char *display_check_err_msg = display_available (&dpy_avail);
388
389 if (! dpy_avail)
390 {
391 start_gui = false;
392 gui_libs = false;
393
394 file = octave_cli;
395
396 if (warn_display)
397 {
398 if (! display_check_err_msg)
399 display_check_err_msg = "graphical display unavailable";
400
401 std::cerr << "octave: " << display_check_err_msg << std::endl;
402 std::cerr << "octave: disabling GUI features" << std::endl;
403 }
404 }
405 }
406
407 #if defined (OCTAVE_USE_WINDOWS_API)
408 file += ".exe";
409 #endif
410
411 new_argv[0] = strsave (file.c_str ());
412
413 // The Octave interpreter may be multithreaded. If so, we attempt to
414 // ensure that signals are delivered to the main interpreter thread
415 // and no others by blocking signals before we exec the Octave
416 // interpreter executable. When that process starts, it will unblock
417 // signals in the main interpreter thread. When running the GUI as a
418 // subprocess, we also unblock signals that the parent process handles
419 // so we can forward them to the child.
420
421 octave_block_async_signals ();
422 octave_block_signal_by_name ("SIGTSTP");
423
424 #if defined (HAVE_OCTAVE_QT_GUI) && ! defined (OCTAVE_USE_WINDOWS_API)
425
426 if (gui_libs && start_gui)
427 {
428 // Fork and exec when starting the GUI so that we will call
429 // setsid to give up the controlling terminal (if any) and so that
430 // the GUI process will be in a separate process group.
431 //
432 // The GUI process must be in a separate process group so that we
433 // can send an interrupt signal to all child processes when
434 // interrupting the interpreter. See also bug #49609 and the
435 // function pthread_thread_manager::interrupt in the file
436 // libgui/src/thread-manager.cc.
437
438 gui_pid = octave_fork_wrapper ();
439
440 if (gui_pid < 0)
441 {
442 std::cerr << "octave: fork failed!" << std::endl;
443
444 retval = 1;
445 }
446 else if (gui_pid == 0)
447 {
448 // Child.
449
450 if (octave_setsid_wrapper () < 0)
451 {
452 std::cerr << "octave: error calling setsid!" << std::endl;
453
454 retval = 1;
455 }
456 else
457 retval = octave_exec (file, new_argv);
458 }
459 else
460 {
461 // Parent. Forward signals to child while waiting for it to exit.
462
463 install_signal_handlers ();
464
465 octave_unblock_async_signals ();
466 octave_unblock_signal_by_name ("SIGTSTP");
467
468 int status;
469
470 while (true)
471 {
472 octave_waitpid_wrapper (gui_pid, &status, 0);
473
474 if (caught_signal > 0)
475 {
476 int sig = caught_signal;
477
478 caught_signal = -1;
479
480 octave_kill_wrapper (gui_pid, sig);
481 }
482 else if (octave_wifexited_wrapper (status))
483 {
484 retval = octave_wexitstatus_wrapper (status);
485 break;
486 }
487 else if (octave_wifsignaled_wrapper (status))
488 {
489 std::cerr << "octave exited with signal "
490 << octave_wtermsig_wrapper (status) << std::endl;
491 break;
492 }
493 }
494 }
495 }
496 else
497 {
498 retval = octave_exec (file, new_argv);
499
500 if (retval < 0)
501 std::cerr << argv[0] << ": " << std::strerror (errno) << std::endl;
502 }
503
504 #else
505
506 retval = octave_exec (file, new_argv);
507
508 if (retval < 0)
509 std::cerr << argv[0] << ": " << std::strerror (errno) << std::endl;
510
511 #endif
512
513 return retval;
514 }
515