1 // This file is part of BOINC.
2 // http://boinc.berkeley.edu
3 // Copyright (C) 2008 University of California
4 //
5 // BOINC is free software; you can redistribute it and/or modify it
6 // under the terms of the GNU Lesser General Public License
7 // as published by the Free Software Foundation,
8 // either version 3 of the License, or (at your option) any later version.
9 //
10 // BOINC 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.
13 // See the GNU Lesser General Public License for more details.
14 //
15 // You should have received a copy of the GNU Lesser General Public License
16 // along with BOINC.  If not, see <http://www.gnu.org/licenses/>.
17 
18 // command-line parsing, and handling of 1-time actions
19 
20 #include "cpp.h"
21 
22 #ifdef _WIN32
23 #include "boinc_win.h"
24 #ifdef _MSC_VER
25 #define chdir _chdir
26 #endif
27 #else
28 #include "config.h"
29 #include <cstdio>
30 #include <unistd.h>
31 #endif
32 
33 #include "str_util.h"
34 #include "url.h"
35 #include "str_replace.h"
36 #include "util.h"
37 
38 #include "client_msgs.h"
39 #include "client_state.h"
40 #include "cs_proxy.h"
41 #include "main.h"
42 #include "project.h"
43 #include "sandbox.h"
44 
print_options(char * prog)45 static void print_options(char* prog) {
46     printf(
47         "The command-line options for %s are intended for debugging.\n"
48         "The recommended command-line interface is a separate program,'boinccmd'.\n"
49         "Run boinccmd in the same directory as %s.\n"
50         "\n"
51         "Usage: %s [options]\n"
52         "    --abort_jobs_on_exit           when client exits, abort and report jobs\n"
53         "    --allow_remote_gui_rpc         allow remote GUI RPC connections\n"
54         "    --allow_multiple_clients       allow >1 instances per host\n"
55         "    --attach_project <URL> <key>   attach to a project\n"
56         "    --check_all_logins             for idle detection, check remote logins too\n"
57         "    --daemon                       run as daemon (Unix)\n"
58         "    --detach_console               detach from console (Windows)\n"
59         "    --detach_project <URL>         detach from a project\n"
60         "    --dir <path>                   use given dir as BOINC home\n"
61         "    --exit_after_app_start N       exit N seconds after an app starts\n"
62         "    --exit_after_finish            exit right after finishing a job\n"
63         "    --exit_before_start            exit right before starting a job\n"
64         "    --exit_before_upload           exit right before starting an upload \n"
65         "    --exit_when_idle               exit when there are no results\n"
66         "    --fetch_minimal_work           fetch only 1 job per device\n"
67         "    --file_xfer_giveup_period N    give up on file xfers after N sec\n"
68         "    --gui_rpc_port <port>          port for GUI RPCs\n"
69         "    --gui_rpc_unix_domain          use Unix domain for GUI RPCs\n"
70         "    --help                         show options\n"
71         "    --insecure                     disable app sandboxing (Unix)\n"
72 #ifdef __APPLE__
73         "    --launched_by_manager          client was launched by Manager\n"
74 #endif
75         "    --master_fetch_interval N      limiting period of master retry\n"
76         "    --master_fetch_period N        reload master URL after N RPC failures\n"
77         "    --master_fetch_retry_cap N     exponential backoff limit\n"
78         "    --no_gpus                      don't check for GPUs\n"
79         "    --no_gui_rpc                   don't allow GUI RPC, don't make socket\n"
80         "    --no_info_fetch                don't fetch project list or client version info\n"
81         "    --no_priority_change           run apps at same priority as client\n"
82         "    --pers_giveup N                giveup time for persistent file xfer\n"
83         "    --pers_retry_delay_max N       max for file xfer exponential backoff\n"
84         "    --pers_retry_delay_min N       min for file xfer exponential backoff\n"
85         "    --redirectio                   redirect stdout and stderr to log files\n"
86         "    --reset_project <URL>          reset (clear) a project\n"
87         "    --retry_cap N                  exponential backoff limit\n"
88         "    --run_cpu_benchmarks           run the CPU benchmarks\n"
89         "    --run_by_updater               set by updater\n"
90 #ifdef __APPLE__
91         "    --saver                        client was launched by screensaver\n"
92 #endif
93         "    --sched_retry_delay_max N      max for RPC exponential backoff\n"
94         "    --sched_retry_delay_min N      min for RPC exponential backoff\n"
95         "    --show_projects                show attached projects\n"
96         "    --skip_cpu_benchmarks          don't run CPU benchmarks\n"
97         "    --start_delay X                delay starting apps for X secs\n"
98         "    --suppress_net_info            don't send network addrs to server\n"
99         "    --unsigned_apps_ok             allow unsigned apps (for testing)\n"
100         "    --update_prefs <URL>           contact a project to update preferences\n"
101         "    --version                      show version info\n"
102         ,
103         prog, prog, prog
104     );
105 }
106 
107 // Parse the command line arguments passed to the client
108 // NOTE: init() has not been called at this point
109 // (i.e. client_state.xml has not been parsed)
110 // So just record the args here.
111 // The actions get done in do_cmdline_actions()
112 //
113 // Check for both -X (deprecated) and --X
114 //
115 #define ARGX2(s1,s2) (!strcmp(argv[i], s1)||!strcmp(argv[i], s2))
116 #define ARG(S) ARGX2("-"#S, "--"#S)
117 
parse_cmdline(int argc,char ** argv)118 void CLIENT_STATE::parse_cmdline(int argc, char** argv) {
119     int i;
120     bool show_options = false;
121 
122     // NOTE: if you change or add anything, make the same change
123     // in show_options() (above) and in doc/client.php
124 
125     for (i=1; i<argc; i++) {
126         if (0) {
127         } else if (ARG(abort_jobs_on_exit)) {
128             cc_config.abort_jobs_on_exit = true;
129         } else if (ARG(allow_multiple_clients)) {
130             cc_config.allow_multiple_clients = true;
131         } else if (ARG(allow_remote_gui_rpc)) {
132             cc_config.allow_remote_gui_rpc = true;
133         } else if (ARG(attach_project)) {
134             if (i >= argc-2) {
135                 show_options = true;
136             } else {
137                 safe_strcpy(attach_project_url, argv[++i]);
138                 safe_strcpy(attach_project_auth, argv[++i]);
139             }
140         } else if (ARG(check_all_logins)) {
141             check_all_logins = true;
142         } else if (ARG(daemon)) {
143             executing_as_daemon = true;
144         } else if (ARG(detach_phase_two) || ARG(detach) || ARG(detach_console)) {
145             detach_console = true;
146         } else if (ARG(detach_project)) {
147             if (i == argc-1) show_options = true;
148             else safe_strcpy(detach_project_url, argv[++i]);
149         } else if (ARG(dir)) {
150             if (i == argc-1) {
151                 show_options = true;
152             } else {
153                 if (chdir(argv[++i])) {
154                     perror("chdir");
155                     exit(1);
156                 }
157             }
158         } else if (ARG(exit_after_app_start)) {
159             if (i == argc-1) show_options = true;
160             else exit_after_app_start_secs = atoi(argv[++i]);
161         } else if (ARG(exit_after_finish)) {
162             cc_config.exit_after_finish = true;
163         } else if (ARG(exit_before_start)) {
164             cc_config.exit_before_start = true;
165         } else if (ARG(exit_before_upload)) {
166             exit_before_upload = true;
167         } else if (ARG(exit_when_idle)) {
168             cc_config.exit_when_idle = true;
169             cc_config.report_results_immediately = true;
170         } else if (ARG(fetch_minimal_work)) {
171             cc_config.fetch_minimal_work = true;
172         } else if (ARG(file_xfer_giveup_period)) {
173             if (i == argc-1) show_options = true;
174             else file_xfer_giveup_period = atoi(argv[++i]);
175         } else if (ARG(gui_rpc_port)) {
176             if (i == argc-1) show_options = true;
177             else cmdline_gui_rpc_port = atoi(argv[++i]);
178         } else if (ARG(gui_rpc_unix_domain)) {
179             gui_rpc_unix_domain = true;
180         } else if (ARG(help)) {
181             print_options(argv[0]);
182             exit(0);
183         } else if (ARG(insecure)) {
184             g_use_sandbox = false;
185         } else if (ARG(launched_by_manager)) {
186             launched_by_manager = true;
187         } else if (ARG(master_fetch_interval)) {
188             if (i == argc-1) show_options = true;
189             else master_fetch_interval = atoi(argv[++i]);
190         } else if (ARG(master_fetch_period)) {
191             if (i == argc-1) show_options = true;
192             else master_fetch_period = atoi(argv[++i]);
193         } else if (ARG(master_fetch_retry_cap)) {
194             if (i == argc-1) show_options = true;
195             else master_fetch_retry_cap = atoi(argv[++i]);
196         } else if (ARG(no_gpus)) {
197             cc_config.no_gpus = true;
198         } else if (ARG(no_gui_rpc)) {
199             no_gui_rpc = true;
200         } else if (ARG(no_info_fetch)) {
201             cc_config.no_info_fetch = true;
202         } else if (ARG(no_priority_change)) {
203             cc_config.no_priority_change = true;
204         } else if (ARG(pers_giveup)) {
205             if (i == argc-1) show_options = true;
206             else pers_giveup = atoi(argv[++i]);
207         } else if (ARG(pers_retry_delay_max)) {
208             if (i == argc-1) show_options = true;
209             else pers_retry_delay_max = atoi(argv[++i]);
210         } else if (ARG(pers_retry_delay_min)) {
211             if (i == argc-1) show_options = true;
212             else pers_retry_delay_min = atoi(argv[++i]);
213         } else if (!strncmp(argv[i], "-psn_", strlen("-psn_"))) {
214             // ignore -psn argument on Mac OS X
215         } else if (ARG(redirectio)) {
216             redirect_io = true;
217         } else if (ARG(reset_project)) {
218             if (i == argc-1) show_options = true;
219             else safe_strcpy(reset_project_url, argv[++i]);
220         } else if (ARG(retry_cap)) {
221             if (i == argc-1) show_options = true;
222             else retry_cap = atoi(argv[++i]);
223         } else if (ARG(run_by_updater)) {
224             run_by_updater = true;
225         } else if (ARG(run_cpu_benchmarks)) {
226             run_cpu_benchmarks = true;
227         } else if (ARG(saver)) {
228             started_by_screensaver = true;
229         } else if (ARG(sched_retry_delay_max)) {
230             if (i == argc-1) show_options = true;
231             else sched_retry_delay_max = atoi(argv[++i]);
232         } else if (ARG(sched_retry_delay_min)) {
233             if (i == argc-1) show_options = true;
234             else sched_retry_delay_min = atoi(argv[++i]);
235         } else if (ARG(show_projects)) {
236             show_projects = true;
237         } else if (ARG(skip_cpu_benchmarks)) {
238             cc_config.skip_cpu_benchmarks = true;
239         } else if (ARG(start_delay)) {
240             if (i == argc-1) show_options = true;
241             else cc_config.start_delay = atof(argv[++i]);
242         } else if (ARG(suppress_net_info)) {
243             cc_config.suppress_net_info = true;
244         } else if (ARG(unsigned_apps_ok)) {
245             cc_config.unsigned_apps_ok = true;
246             cc_config.dont_check_file_sizes = true;
247         } else if (ARG(update_prefs)) {
248             if (i == argc-1) show_options = true;
249             else safe_strcpy(update_prefs_url, argv[++i]);
250         } else if (ARG(version)) {
251 #ifdef __APPLE__
252             CLIENT_STATE cs;
253             cs.detect_platforms();
254             printf(BOINC_VERSION_STRING " %s\n", cs.get_primary_platform());
255 #else
256             printf(BOINC_VERSION_STRING " " HOSTTYPE "\n");
257 #endif
258             exit(0);
259 #ifdef __APPLE__
260         // workaround for bug in XCode 4.2: accept but ignore
261         // argument -NSDocumentRevisionsDebugMode=YES
262         } else if (ARG(NSDocumentRevisionsDebugMode)) {
263             ++i;
264 #endif
265         // detect_gpus is for internal use only - do not
266         // add it to show_options() or doc/client.php
267         // This statement just avoids Unknown option warning
268         } else if (ARG(detect_gpus)) {
269         } else {
270             printf("Unknown option: %s\n", argv[i]);
271             show_options = true;
272         }
273     }
274     if (show_options) {
275         print_options(argv[0]);
276         exit(1);
277     }
278 }
279 
280 #undef ARG
281 #undef ARGX2
282 
parse_env_vars()283 void CLIENT_STATE::parse_env_vars() {
284     char *p;
285     PARSED_URL purl;
286 
287     p = getenv("HTTP_PROXY");
288     if (p && strlen(p) > 0) {
289         parse_url(p, purl);
290         switch (purl.protocol) {
291         case URL_PROTOCOL_HTTP:
292         case URL_PROTOCOL_HTTPS:
293             env_var_proxy_info.present = true;
294             env_var_proxy_info.use_http_proxy = true;
295             safe_strcpy(env_var_proxy_info.http_user_name, purl.user);
296             safe_strcpy(env_var_proxy_info.http_user_passwd, purl.passwd);
297             safe_strcpy(env_var_proxy_info.http_server_name, purl.host);
298             env_var_proxy_info.http_server_port = purl.port;
299             break;
300         default:
301             msg_printf_notice(0, false,
302                 "http://boinc.berkeley.edu/manager_links.php?target=notice&controlid=proxy_env",
303                 _("The HTTP_PROXY environment variable must specify an HTTP proxy")
304             );
305         }
306     }
307     p = getenv("HTTP_USER_NAME");
308     if (p) {
309         env_var_proxy_info.use_http_auth = true;
310         safe_strcpy(env_var_proxy_info.http_user_name, p);
311         p = getenv("HTTP_USER_PASSWD");
312         if (p) {
313             safe_strcpy(env_var_proxy_info.http_user_passwd, p);
314         }
315     }
316 
317     p = getenv("SOCKS_SERVER");
318     if (!p) p = getenv("SOCKS5_SERVER");
319     if (p && strlen(p)) {
320         parse_url(p, purl);
321         env_var_proxy_info.present = true;
322         env_var_proxy_info.use_socks_proxy = true;
323         safe_strcpy(env_var_proxy_info.socks5_user_name, purl.user);
324         safe_strcpy(env_var_proxy_info.socks5_user_passwd, purl.passwd);
325         safe_strcpy(env_var_proxy_info.socks_server_name, purl.host);
326         env_var_proxy_info.socks_server_port = purl.port;
327     }
328 
329     p = getenv("SOCKS5_USER");
330     if (!p) p = getenv("SOCKS_USER");
331     if (p) {
332         safe_strcpy(env_var_proxy_info.socks5_user_name, p);
333     }
334 
335     p = getenv("SOCKS5_PASSWD");
336     if (p) {
337         safe_strcpy(env_var_proxy_info.socks5_user_passwd, p);
338     }
339 }
340 
do_cmdline_actions()341 void CLIENT_STATE::do_cmdline_actions() {
342     unsigned int i;
343 
344     if (show_projects) {
345         printf("projects:\n");
346         for (i=0; i<projects.size(); i++) {
347             msg_printf(NULL, MSG_INFO, "URL: %s name: %s\n",
348                 projects[i]->master_url, projects[i]->project_name
349             );
350         }
351         exit(0);
352     }
353 
354     if (strlen(detach_project_url)) {
355         canonicalize_master_url(detach_project_url, sizeof(detach_project_url));
356         PROJECT* project = lookup_project(detach_project_url);
357         if (project) {
358             // do this before detaching - it frees the project
359             //
360             msg_printf(project, MSG_INFO, "detaching from %s\n", detach_project_url);
361             detach_project(project);
362         } else {
363             msg_printf(NULL, MSG_INFO, "project %s not found\n", detach_project_url);
364         }
365         exit(0);
366     }
367 
368     if (strlen(reset_project_url)) {
369         canonicalize_master_url(reset_project_url, sizeof(reset_project_url));
370         PROJECT* project = lookup_project(reset_project_url);
371         if (project) {
372             reset_project(project, false);
373             msg_printf(project, MSG_INFO, "Project %s has been reset", reset_project_url);
374         } else {
375             msg_printf(NULL, MSG_INFO, "project %s not found\n", reset_project_url);
376         }
377         exit(0);
378     }
379 
380     if (strlen(update_prefs_url)) {
381         canonicalize_master_url(update_prefs_url, sizeof(update_prefs_url));
382         PROJECT* project = lookup_project(update_prefs_url);
383         if (project) {
384             project->sched_rpc_pending = RPC_REASON_USER_REQ;
385         } else {
386             msg_printf(NULL, MSG_INFO, "project %s not found\n", update_prefs_url);
387         }
388     }
389 
390     if (strlen(attach_project_url)) {
391         canonicalize_master_url(attach_project_url, sizeof(attach_project_url));
392         add_project(attach_project_url, attach_project_auth, "", false);
393     }
394 }
395 
396