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