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