1 /**************************************************************************
2 *   Copyright (C) 2005-2020 by Oleksandr Shneyder                         *
3 *                              <o.shneyder@phoca-gmbh.de>                 *
4 *   Copyright (C) 2015-2020 by Mihai Moldovan <ionic@ionic.de>            *
5 *                                                                         *
6 *   This program 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 2 of the License, or     *
9 *   (at your option) any later version.                                   *
10 *   This program is distributed in the hope that it will be useful,       *
11 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
12 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
13 *   GNU General Public License for more details.                          *
14 *                                                                         *
15 *   You should have received a copy of the GNU General Public License     *
16 *   along with this program.  If not, see <https://www.gnu.org/licenses/>. *
17 ***************************************************************************/
18 
19 #include <iostream>
20 #include <sys/types.h>
21 #include <unistd.h>
22 #include <cstring>
23 #include <cerrno>
24 #include <cstdlib>
25 #include <string>
26 #include <algorithm>
27 #include <cctype>
28 #include <vector>
29 #include <csignal>
30 
31 #include "unixhelper.h"
32 #include "ongetpass.h"
33 #include "compat.h"
34 
wrap_x2go_main(int argc,char ** argv)35 int wrap_x2go_main (int argc, char **argv) {
36   return (x2goMain (argc, argv));
37 }
38 
39 #ifdef Q_OS_UNIX
fork_helper(int argc,char ** argv)40 int fork_helper (int argc, char **argv) {
41   /* Fork off to start helper process. */
42   pid_t tmp_pid = fork ();
43 
44   /* Child. */
45   if (0 == tmp_pid) {
46     /* Starting unixhelper. */
47     std::vector<std::string> new_argv;
48     new_argv.push_back (std::string (argv[0]));
49     new_argv.push_back ("--unixhelper");
50 
51     std::vector<char *> new_argv_c_str;
52     for (std::vector<std::string>::iterator it = new_argv.begin (); it != new_argv.end (); ++it) {
53       const char *elem = (*it).c_str ();
54       new_argv_c_str.push_back (strndup (elem, std::strlen (elem)));
55     }
56 
57     /* Add null pointer as last element. */
58     new_argv_c_str.push_back (0);
59 
60     if (0 != execvp (new_argv_c_str.front (), &(new_argv_c_str.front ()))) {
61       const int saved_errno = errno;
62       std::cerr << "Failed to re-execute process as UNIX cleanup helper tool: " << std::strerror (saved_errno) << "\n"
63                 << "Terminating and killing parent." << "\n"
64                 << "Please report a bug, refer to this documentation: https://wiki.x2go.org/doku.php/wiki:bugs" << std::endl;
65 
66       pid_t parent_pid = getppid ();
67       if (0 != kill (parent_pid, SIGTERM)) {
68         const int saved_errno = errno;
69         std::cerr << "Failed to kill parent process: " << std::strerror (saved_errno) << std::endl;
70       }
71 
72       std::exit (EXIT_FAILURE);
73     }
74 
75     /* Anything here shall be unreachable. */
76     return (0);
77   }
78   /* Error. */
79   else if (-1 == tmp_pid) {
80     const int saved_errno = errno;
81     std::cerr << "Unable to create a new process for the UNIX cleanup watchdog: " << std::strerror (saved_errno) << "\n";
82     std::cerr << "Terminating. Please report a bug, refer to this documentation: https://wiki.x2go.org/doku.php/wiki:bugs" << std::endl;
83 
84     std::exit (EXIT_FAILURE);
85   }
86   /* Parent. */
87   else {
88     /* Start real X2Go Client. */
89     return (wrap_x2go_main (argc, argv));
90   }
91 }
92 #endif /* defined (Q_OS_UNIX) */
93 
main(int argc,char ** argv)94 int main (int argc, char **argv) {
95 #ifdef Q_OS_UNIX
96   /*
97    * Flags we don't need a cleanup helper for, since we know that X2Go Client
98    * will never spawn other processes.
99    *
100    * FIXME: What we'd actually want to have at this point (instead of a
101    *        hardcoded list of parameters, anyway) is to use the argument parser
102    *        from ONMainWindow (parseParameter). If this function returns false
103    *        for any parameter, we know that we won't ever need the UNIX cleanup
104    *        helper tool. Sadly, ONMainWindow is only started/available later,
105    *        so we can't use any of its functionality here. We'd also need to
106    *        make the function side-effect free. It should probably be
107    *        refactored into a special options parser class.
108    */
109   const std::string bypass_flags[] = {
110     "--bypass-cleanup-helper",
111     "--help",
112     "--help-pack",
113     "--version",
114     "-v",
115     "--changelog",
116     "--git-info"
117   };
118 
119   bool bypass_unix_helper = 0;
120   bool unix_helper_request = 0;
121   for (int i = 0; i < argc; ++i) {
122     /* No need to continue scanning if we got the information we were looking for. */
123     if ((bypass_unix_helper) && (unix_helper_request)) {
124       break;
125     }
126 
127     std::string cur_arg (argv[i]);
128 
129     /* Make the current argument lowercase. */
130     std::transform (cur_arg.begin (), cur_arg.end (), cur_arg.begin (), ::tolower);
131 
132     if (!(cur_arg.empty ())) {
133       /* Scan program arguments for --unixhelper flag. */
134       if ((!(unix_helper_request)) && (0 == cur_arg.compare ("--unixhelper"))) {
135         unix_helper_request = 1;
136       }
137 
138       /* Scan for flags bypassing the unix helper. */
139       if (!(bypass_unix_helper)) {
140         for (std::size_t y = 0; y < (sizeof (bypass_flags) / sizeof (*bypass_flags)); ++y) {
141           if (0 == cur_arg.compare (bypass_flags[y])) {
142             bypass_unix_helper = 1;
143           }
144         }
145       }
146     }
147   }
148 
149   /* Sanity checks! */
150   if ((unix_helper_request) && (bypass_unix_helper)) {
151     std::cerr << "Re-execution in UNIX cleanup helper mode was requested, but a command line parameter that is supposed to "
152               << "disable the UNIX cleanup helper was found.\n"
153               << "Terminating. Please report a bug, refer to this documentation: https://wiki.x2go.org/doku.php/wiki:bugs" << std::endl;
154 
155     return (EXIT_FAILURE);
156   }
157 
158   if (bypass_unix_helper) {
159     return (wrap_x2go_main (argc, argv));
160   }
161   else {
162     if (unix_helper_request) {
163       /* We were instructed to start as the UNIX cleanup helper tool. */
164       return (unixhelper::unix_cleanup (getppid ()));
165     }
166     else {
167       /*
168        * setsid() may succeed and we become a session and process
169        * group leader, or it may fail indicating that we already
170        * are a process group leader. Either way is fine.
171        */
172       setsid ();
173 
174       /* We should be process group leader by now. */
175       return (fork_helper (argc, argv));
176     }
177   }
178 #else /* defined (Q_OS_UNIX) */
179   return (wrap_x2go_main (argc, argv));
180 #endif /* defined (Q_OS_UNIX) */
181 }
182