1 /* vifm
2  * Copyright (C) 2001 Ken Steen.
3  * Copyright (C) 2011 xaizek.
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
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, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
18  */
19 
20 #include "background.h"
21 
22 #ifdef _WIN32
23 #include <windows.h>
24 #endif
25 
26 #include <fcntl.h> /* open() */
27 #include <sys/stat.h> /* O_RDONLY */
28 #include <sys/types.h> /* pid_t ssize_t */
29 #ifndef _WIN32
30 #include <sys/wait.h> /* WEXITSTATUS() WIFEXITED() */
31 #endif
32 #include <signal.h> /* kill() */
33 #include <unistd.h> /* execve() fork() */
34 
35 #include <assert.h> /* assert() */
36 #include <errno.h> /* errno */
37 #include <stddef.h> /* NULL wchar_t */
38 #include <stdint.h> /* uintptr_t */
39 #include <stdlib.h> /* EXIT_FAILURE _Exit() free() malloc() */
40 #include <string.h> /* strdup() */
41 
42 #include "cfg/config.h"
43 #include "compat/pthread.h"
44 #include "engine/var.h"
45 #include "engine/variables.h"
46 #include "modes/dialogs/msg_dialog.h"
47 #include "ui/cancellation.h"
48 #include "ui/statusline.h"
49 #include "ui/ui.h"
50 #include "utils/cancellation.h"
51 #include "utils/env.h"
52 #include "utils/fs.h"
53 #include "utils/log.h"
54 #include "utils/path.h"
55 #include "utils/selector.h"
56 #include "utils/str.h"
57 #include "utils/utils.h"
58 #include "cmd_completion.h"
59 #include "status.h"
60 
61 /**
62  * This unit implements three kinds of backgrounded operations:
63  *  - external applications run from vifm (commands);
64  *  - threads that perform auxiliary work (tasks), like counting size of
65  *    directories;
66  *  - threads that perform important work (operations), like file copying,
67  *    deletion, etc.
68  *
69  * All jobs can be viewed via :jobs menu.
70  *
71  * Tasks and operations can provide progress information for displaying it in
72  * UI.
73  *
74  * Operations are displayed on designated job bar.
75  *
76  * On non-Windows systems background thread reads data from error streams of
77  * external applications, which are then displayed by main thread.  This thread
78  * maintains its own list of jobs (via err_next field), which is added to by
79  * building a temporary list with new_err_jobs pointing to its head.  Every job
80  * that has associated external process has the following life cycle:
81  *  1. Created by main thread and passed to error thread through new_err_jobs.
82  *  2. Either gets marked by signal handler or its stream reaches EOF.
83  *  3. Its in_use flag is reset.
84  *  4. Main thread frees corresponding entry.
85  */
86 
87 /* Turns pointer (P) to field (F) of a structure (S) to address of that
88  * structure. */
89 #define STRUCT_FROM_FIELD(S, F, P) ((S *)((char *)P - offsetof(S, F)))
90 
91 /* Special value of process id for internal tasks running in background
92  * threads. */
93 #define WRONG_PID ((pid_t)-1)
94 
95 /* Size of error message reading buffer. */
96 #define ERR_MSG_LEN 1025
97 
98 /* Value of job communication mean for internal jobs. */
99 #ifndef _WIN32
100 #define NO_JOB_ID (-1)
101 #else
102 #define NO_JOB_ID INVALID_HANDLE_VALUE
103 #endif
104 
105 /* Structure with passed to background_task_bootstrap() so it can perform
106  * correct initialization/cleanup. */
107 typedef struct
108 {
109 	bg_task_func func; /* Function to execute in a background thread. */
110 	void *args;        /* Argument to pass. */
111 	bg_job_t *job;     /* Job identifier that corresponds to the task. */
112 }
113 background_task_args;
114 
115 static void set_jobcount_var(int count);
116 static void job_check(bg_job_t *job);
117 static void job_free(bg_job_t *job);
118 static void * error_thread(void *p);
119 static void update_error_jobs(bg_job_t **jobs);
120 static void free_drained_jobs(bg_job_t **jobs);
121 static void import_error_jobs(bg_job_t **jobs);
122 static void make_ready_list(const bg_job_t *jobs, selector_t *selector);
123 #ifndef _WIN32
124 static void report_error_msg(const char title[], const char text[]);
125 #endif
126 static void append_error_msg(bg_job_t *job, const char err_msg[]);
127 static bg_job_t * add_background_job(pid_t pid, const char cmd[],
128 		uintptr_t err, uintptr_t data, BgJobType type);
129 static void * background_task_bootstrap(void *arg);
130 static void set_current_job(bg_job_t *job);
131 static void make_current_job_key(void);
132 static int bg_op_cancel(bg_op_t *bg_op);
133 
134 bg_job_t *bg_jobs = NULL;
135 
136 /* Head of list of newly started jobs. */
137 static bg_job_t *new_err_jobs;
138 /* Mutex to protect new_err_jobs. */
139 static pthread_mutex_t new_err_jobs_lock = PTHREAD_MUTEX_INITIALIZER;
140 /* Conditional variable to signal availability of new jobs in new_err_jobs. */
141 static pthread_cond_t new_err_jobs_cond = PTHREAD_COND_INITIALIZER;
142 
143 /* Thread local storage for bg_job_t associated with active thread. */
144 static pthread_key_t current_job;
145 
146 void
bg_init(void)147 bg_init(void)
148 {
149 	pthread_t id;
150 	const int err = pthread_create(&id, NULL, &error_thread, NULL);
151 	assert(err == 0);
152 	(void)err;
153 
154 	/* Initialize state for the main thread. */
155 	set_current_job(NULL);
156 }
157 
158 void
bg_process_finished_cb(pid_t pid,int exit_code)159 bg_process_finished_cb(pid_t pid, int exit_code)
160 {
161 	bg_job_t *job;
162 
163 	/* Mark any finished jobs. */
164 	job = bg_jobs;
165 	while(job != NULL)
166 	{
167 		if(job->pid == pid)
168 		{
169 			pthread_spin_lock(&job->status_lock);
170 			job->running = 0;
171 			job->exit_code = exit_code;
172 			pthread_spin_unlock(&job->status_lock);
173 			break;
174 		}
175 		job = job->next;
176 	}
177 }
178 
179 void
bg_check(void)180 bg_check(void)
181 {
182 	bg_job_t *head = bg_jobs;
183 
184 	/* Quit if there is no jobs or list is unavailable (e.g. used by another
185 	 * invocation of this function). */
186 	if(head == NULL)
187 	{
188 		set_jobcount_var(0);
189 		return;
190 	}
191 
192 	if(bg_jobs_freeze() != 0)
193 	{
194 		return;
195 	}
196 
197 	int active_jobs = 0;
198 
199 	head = bg_jobs;
200 	bg_jobs = NULL;
201 
202 	bg_job_t *p = head;
203 	bg_job_t *prev = NULL;
204 	while(p != NULL)
205 	{
206 		job_check(p);
207 
208 		pthread_spin_lock(&p->status_lock);
209 		int can_remove = (!p->running && !p->in_use);
210 		active_jobs += (p->running != 0);
211 		pthread_spin_unlock(&p->status_lock);
212 
213 		/* Remove job if it is finished now. */
214 		if(can_remove)
215 		{
216 			bg_job_t *j = p;
217 			if(prev != NULL)
218 				prev->next = p->next;
219 			else
220 				head = p->next;
221 
222 			p = p->next;
223 
224 			if(j->type == BJT_OPERATION)
225 			{
226 				ui_stat_job_bar_remove(&j->bg_op);
227 			}
228 
229 			job_free(j);
230 		}
231 		else
232 		{
233 			prev = p;
234 			p = p->next;
235 		}
236 	}
237 
238 	assert(bg_jobs == NULL && "Job list shouldn't be used by anyone.");
239 	bg_jobs = head;
240 
241 	bg_jobs_unfreeze();
242 
243 	set_jobcount_var(active_jobs);
244 }
245 
246 /* Updates builtin variable that holds number of active jobs.  Schedules UI
247  * redraw on change. */
248 static void
set_jobcount_var(int count)249 set_jobcount_var(int count)
250 {
251 	int old_count = var_to_int(getvar("v:jobcount"));
252 	if(count != old_count)
253 	{
254 		var_t var = var_from_int(count);
255 		setvar("v:jobcount", var);
256 		var_free(var);
257 
258 		stats_redraw_later();
259 	}
260 }
261 
262 /* Checks status of the job.  Processes error stream or checks whether process
263  * is still running. */
264 static void
job_check(bg_job_t * job)265 job_check(bg_job_t *job)
266 {
267 	char *new_errors;
268 
269 	/* Display portions of errors from the job while there are any. */
270 	do
271 	{
272 		pthread_spin_lock(&job->errors_lock);
273 		new_errors = job->new_errors;
274 		job->new_errors = NULL;
275 		job->new_errors_len = 0U;
276 		pthread_spin_unlock(&job->errors_lock);
277 
278 		if(new_errors != NULL && !job->skip_errors)
279 		{
280 			job->skip_errors = prompt_error_msg("Background Process Error",
281 					new_errors);
282 		}
283 		free(new_errors);
284 	}
285 	while(new_errors != NULL);
286 
287 #ifdef _WIN32
288 	DWORD retcode;
289 	if(GetExitCodeProcess(job->hprocess, &retcode) != 0)
290 	{
291 		if(retcode != STILL_ACTIVE)
292 		{
293 			pthread_spin_lock(&job->status_lock);
294 			job->running = 0;
295 			pthread_spin_unlock(&job->status_lock);
296 		}
297 	}
298 #endif
299 }
300 
301 /* Frees resources allocated by the job as well as the bg_job_t structure
302  * itself.  The job can be NULL. */
303 static void
job_free(bg_job_t * job)304 job_free(bg_job_t *job)
305 {
306 	if(job == NULL)
307 	{
308 		return;
309 	}
310 
311 	pthread_spin_destroy(&job->errors_lock);
312 	pthread_spin_destroy(&job->status_lock);
313 	if(job->type != BJT_COMMAND)
314 	{
315 		pthread_spin_destroy(&job->bg_op_lock);
316 	}
317 
318 #ifndef _WIN32
319 	if(job->err_stream != NO_JOB_ID)
320 	{
321 		close(job->err_stream);
322 	}
323 #else
324 	if(job->err_stream != NO_JOB_ID)
325 	{
326 		CloseHandle(job->err_stream);
327 	}
328 	if(job->hprocess != NO_JOB_ID)
329 	{
330 		CloseHandle(job->hprocess);
331 	}
332 #endif
333 	free(job->bg_op.descr);
334 	free(job->cmd);
335 	free(job->new_errors);
336 	free(job->errors);
337 	free(job);
338 }
339 
340 int
bg_and_wait_for_errors(char cmd[],const struct cancellation_t * cancellation)341 bg_and_wait_for_errors(char cmd[], const struct cancellation_t *cancellation)
342 {
343 #ifndef _WIN32
344 	pid_t pid;
345 	int error_pipe[2];
346 	int result = 0;
347 
348 	if(pipe(error_pipe) != 0)
349 	{
350 		report_error_msg("File pipe error", "Error creating pipe");
351 		return -1;
352 	}
353 
354 	(void)set_sigchld(1);
355 
356 	if((pid = fork()) == -1)
357 	{
358 		(void)set_sigchld(0);
359 		return -1;
360 	}
361 
362 	if(pid == 0)
363 	{
364 		(void)set_sigchld(0);
365 		run_from_fork(error_pipe, 1, 0, cmd, SHELL_BY_APP);
366 	}
367 	else
368 	{
369 		char buf[80*10];
370 		char linebuf[80];
371 		int nread = 0;
372 
373 		close(error_pipe[1]); /* Close write end of pipe. */
374 
375 		wait_for_data_from(pid, NULL, error_pipe[0], cancellation);
376 
377 		buf[0] = '\0';
378 		while((nread = read(error_pipe[0], linebuf, sizeof(linebuf) - 1)) > 0)
379 		{
380 			const int read_empty_line = nread == 1 && linebuf[0] == '\n';
381 			result = -1;
382 			linebuf[nread] = '\0';
383 
384 			if(!read_empty_line)
385 			{
386 				strncat(buf, linebuf, sizeof(buf) - strlen(buf) - 1);
387 			}
388 
389 			wait_for_data_from(pid, NULL, error_pipe[0], cancellation);
390 		}
391 		close(error_pipe[0]);
392 
393 		if(result != 0)
394 		{
395 			report_error_msg("Background Process Error", buf);
396 		}
397 		else
398 		{
399 			/* Don't use "const int" variables with WEXITSTATUS() as they cause
400 			 * compilation errors in case __USE_BSD is defined.  Anonymous type with
401 			 * "const int" is composed via compound literal expression. */
402 			int status = get_proc_exit_status(pid);
403 			result = (status != -1 && WIFEXITED(status)) ? WEXITSTATUS(status) : -1;
404 		}
405 	}
406 
407 	(void)set_sigchld(0);
408 
409 	return result;
410 #else
411 	return -1;
412 #endif
413 }
414 
415 /* Entry point of a thread which reads input from input of active background
416  * programs.  Does not return. */
417 static void *
error_thread(void * p)418 error_thread(void *p)
419 {
420 	bg_job_t *jobs = NULL;
421 
422 	selector_t *selector = selector_alloc();
423 	if(selector == NULL)
424 	{
425 		return NULL;
426 	}
427 
428 	(void)pthread_detach(pthread_self());
429 	block_all_thread_signals();
430 
431 	while(1)
432 	{
433 		update_error_jobs(&jobs);
434 		make_ready_list(jobs, selector);
435 		while(selector_wait(selector, 250))
436 		{
437 			int need_update_list = (jobs == NULL);
438 
439 			bg_job_t **job = &jobs;
440 			while(*job != NULL)
441 			{
442 				bg_job_t *const j = *job;
443 				char err_msg[ERR_MSG_LEN];
444 				ssize_t nread;
445 
446 				if(!selector_is_ready(selector, j->err_stream))
447 				{
448 					goto next_job;
449 				}
450 
451 #ifndef _WIN32
452 				nread = read(j->err_stream, err_msg, sizeof(err_msg) - 1U);
453 #else
454 				nread = -1;
455 				DWORD bytes_read;
456 				if(ReadFile(j->err_stream, err_msg, sizeof(err_msg) - 1U, &bytes_read,
457 							NULL))
458 				{
459 					nread = bytes_read;
460 				}
461 #endif
462 				if(nread < 0)
463 				{
464 					need_update_list = 1;
465 					j->drained = 1;
466 					goto next_job;
467 				}
468 
469 				if(nread == 0)
470 				{
471 					/* Reached EOF, exclude corresponding file descriptor from the set,
472 					 * cut the job out of our list and allow its deletion. */
473 					selector_remove(selector, j->err_stream);
474 					*job = j->err_next;
475 					pthread_spin_lock(&j->status_lock);
476 					j->in_use = 0;
477 					pthread_spin_unlock(&j->status_lock);
478 					continue;
479 				}
480 
481 				err_msg[nread] = '\0';
482 				append_error_msg(j, err_msg);
483 
484 			next_job:
485 				job = &j->err_next;
486 			}
487 
488 			if(!need_update_list)
489 			{
490 				pthread_mutex_lock(&new_err_jobs_lock);
491 				need_update_list = (new_err_jobs != NULL);
492 				pthread_mutex_unlock(&new_err_jobs_lock);
493 			}
494 			if(need_update_list)
495 			{
496 				break;
497 			}
498 		}
499 	}
500 
501 	selector_free(selector);
502 	return NULL;
503 }
504 
505 /* Updates *jobs by removing finished tasks and adding new ones. */
506 static void
update_error_jobs(bg_job_t ** jobs)507 update_error_jobs(bg_job_t **jobs)
508 {
509 	free_drained_jobs(jobs);
510 	import_error_jobs(jobs);
511 }
512 
513 /* Updates *jobs by removing finished tasks. */
514 static void
free_drained_jobs(bg_job_t ** jobs)515 free_drained_jobs(bg_job_t **jobs)
516 {
517 	bg_job_t **job = jobs;
518 	while(*job != NULL)
519 	{
520 		bg_job_t *const j = *job;
521 
522 		if(j->drained)
523 		{
524 			pthread_spin_lock(&j->status_lock);
525 			/* If finished, reset in_use mark and drop it from the list. */
526 			if(!j->running)
527 			{
528 				j->in_use = 0;
529 				*job = j->err_next;
530 				pthread_spin_unlock(&j->status_lock);
531 				continue;
532 			}
533 			pthread_spin_unlock(&j->status_lock);
534 		}
535 
536 		job = &j->err_next;
537 	}
538 }
539 
540 /* Updates *jobs by adding new tasks. */
541 static void
import_error_jobs(bg_job_t ** jobs)542 import_error_jobs(bg_job_t **jobs)
543 {
544 	bg_job_t *new_jobs;
545 
546 	/* Add new tasks to internal list, wait if there are no jobs. */
547 	pthread_mutex_lock(&new_err_jobs_lock);
548 	while(*jobs == NULL && new_err_jobs == NULL)
549 	{
550 		pthread_cond_wait(&new_err_jobs_cond, &new_err_jobs_lock);
551 	}
552 	new_jobs = new_err_jobs;
553 	new_err_jobs = NULL;
554 	pthread_mutex_unlock(&new_err_jobs_lock);
555 
556 	/* Prepend new jobs to the list. */
557 	while(new_jobs != NULL)
558 	{
559 		bg_job_t *const new_job = new_jobs;
560 		new_jobs = new_jobs->err_next;
561 
562 		assert(new_job->type == BJT_COMMAND &&
563 				"Only external commands should be here.");
564 
565 		/* Mark a this job as an interesting one to avoid it being killed until we
566 		 * have a chance to read error stream. */
567 		new_job->drained = 0;
568 
569 		new_job->err_next = *jobs;
570 		*jobs = new_job;
571 	}
572 }
573 
574 /* Reinitializes the selector with up-to-date list of objects to watch. */
575 static void
make_ready_list(const bg_job_t * jobs,selector_t * selector)576 make_ready_list(const bg_job_t *jobs, selector_t *selector)
577 {
578 	selector_reset(selector);
579 
580 	while(jobs != NULL)
581 	{
582 		selector_add(selector, jobs->err_stream);
583 		jobs = jobs->err_next;
584 	}
585 }
586 
587 #ifndef _WIN32
588 /* Either displays error message to the user for foreground operations or saves
589  * it for displaying on the next invocation of bg_check(). */
590 static void
report_error_msg(const char title[],const char text[])591 report_error_msg(const char title[], const char text[])
592 {
593 	bg_job_t *const job = pthread_getspecific(current_job);
594 	if(job == NULL)
595 	{
596 		ui_cancellation_push_off();
597 		show_error_msg(title, text);
598 		ui_cancellation_pop();
599 	}
600 	else
601 	{
602 		append_error_msg(job, text);
603 	}
604 }
605 #endif
606 
607 /* Appends message to error-related fields of the job. */
608 static void
append_error_msg(bg_job_t * job,const char err_msg[])609 append_error_msg(bg_job_t *job, const char err_msg[])
610 {
611 	pthread_spin_lock(&job->errors_lock);
612 	(void)strappend(&job->errors, &job->errors_len, err_msg);
613 	(void)strappend(&job->new_errors, &job->new_errors_len, err_msg);
614 	pthread_spin_unlock(&job->errors_lock);
615 }
616 
617 #ifndef _WIN32
618 pid_t
bg_run_and_capture(char cmd[],int user_sh,FILE ** out,FILE ** err)619 bg_run_and_capture(char cmd[], int user_sh, FILE **out, FILE **err)
620 {
621 	pid_t pid;
622 	int out_pipe[2];
623 	int error_pipe[2];
624 
625 	if(pipe(out_pipe) != 0)
626 	{
627 		show_error_msg("File pipe error", "Error creating pipe");
628 		return (pid_t)-1;
629 	}
630 
631 	if(pipe(error_pipe) != 0)
632 	{
633 		show_error_msg("File pipe error", "Error creating pipe");
634 		close(out_pipe[0]);
635 		close(out_pipe[1]);
636 		return (pid_t)-1;
637 	}
638 
639 	if((pid = fork()) == -1)
640 	{
641 		close(out_pipe[0]);
642 		close(out_pipe[1]);
643 		close(error_pipe[0]);
644 		close(error_pipe[1]);
645 		return (pid_t)-1;
646 	}
647 
648 	if(pid == 0)
649 	{
650 		char *sh;
651 		char *sh_flag;
652 
653 		close(out_pipe[0]);
654 		close(error_pipe[0]);
655 		if(dup2(out_pipe[1], STDOUT_FILENO) == -1)
656 		{
657 			_Exit(EXIT_FAILURE);
658 		}
659 		if(dup2(error_pipe[1], STDERR_FILENO) == -1)
660 		{
661 			_Exit(EXIT_FAILURE);
662 		}
663 
664 		sh = user_sh ? get_execv_path(cfg.shell) : "/bin/sh";
665 		sh_flag = user_sh ? cfg.shell_cmd_flag : "-c";
666 		prepare_for_exec();
667 		execvp(sh, make_execv_array(sh, sh_flag, cmd));
668 		_Exit(127);
669 	}
670 
671 	close(out_pipe[1]);
672 	close(error_pipe[1]);
673 	*out = fdopen(out_pipe[0], "r");
674 	*err = fdopen(error_pipe[0], "r");
675 
676 	return pid;
677 }
678 #else
679 /* Runs command in a background and redirects its stdout and stderr streams to
680  * file streams which are set.  Returns (pid_t)0 or (pid_t)-1 on error. */
681 static pid_t
background_and_capture_internal(char cmd[],int user_sh,FILE ** out,FILE ** err,int out_pipe[2],int err_pipe[2])682 background_and_capture_internal(char cmd[], int user_sh, FILE **out, FILE **err,
683 		int out_pipe[2], int err_pipe[2])
684 {
685 	const wchar_t *args[4];
686 	char *cwd;
687 	int code;
688 	wchar_t *final_wide_cmd;
689 	wchar_t *wide_sh = NULL;
690 	wchar_t *wide_sh_flag;
691 	const int use_cmd = (!user_sh || curr_stats.shell_type == ST_CMD);
692 
693 	if(_dup2(out_pipe[1], _fileno(stdout)) != 0)
694 		return (pid_t)-1;
695 	if(_dup2(err_pipe[1], _fileno(stderr)) != 0)
696 		return (pid_t)-1;
697 
698 	/* At least cmd.exe is incapable of handling UNC paths. */
699 	cwd = save_cwd();
700 	if(cwd != NULL && is_unc_path(cwd))
701 	{
702 		(void)chdir(get_tmpdir());
703 	}
704 
705 	wide_sh = to_wide(use_cmd ? "cmd" : cfg.shell);
706 	wide_sh_flag = to_wide(user_sh ? cfg.shell_cmd_flag : "/C");
707 
708 	if(use_cmd)
709 	{
710 		final_wide_cmd = to_wide(cmd);
711 
712 		args[0] = wide_sh;
713 		args[1] = wide_sh_flag;
714 		args[2] = final_wide_cmd;
715 		args[3] = NULL;
716 	}
717 	else
718 	{
719 		/* Nobody cares that there is an array of arguments, all arguments just get
720 		 * concatenated anyway...  Therefore we need to take care of escaping stuff
721 		 * ourselves. */
722 		char *const modified_cmd = win_make_sh_cmd(cmd,
723 				user_sh ? SHELL_BY_USER : SHELL_BY_APP);
724 		final_wide_cmd = to_wide(modified_cmd);
725 		free(modified_cmd);
726 
727 		args[0] = final_wide_cmd;
728 		args[1] = NULL;
729 	}
730 
731 	code = _wspawnvp(P_NOWAIT, wide_sh, args);
732 
733 	free(wide_sh_flag);
734 	free(wide_sh);
735 	free(final_wide_cmd);
736 
737 	restore_cwd(cwd);
738 
739 	if(code == 0)
740 	{
741 		return (pid_t)-1;
742 	}
743 
744 	if((*out = _fdopen(out_pipe[0], "r")) == NULL)
745 		return (pid_t)-1;
746 	if((*err = _fdopen(err_pipe[0], "r")) == NULL)
747 	{
748 		fclose(*out);
749 		return (pid_t)-1;
750 	}
751 
752 	return 0;
753 }
754 
755 pid_t
bg_run_and_capture(char cmd[],int user_sh,FILE ** out,FILE ** err)756 bg_run_and_capture(char cmd[], int user_sh, FILE **out, FILE **err)
757 {
758 	int out_fd, out_pipe[2];
759 	int err_fd, err_pipe[2];
760 	pid_t pid;
761 
762 	if(_pipe(out_pipe, 512, O_NOINHERIT) != 0)
763 	{
764 		show_error_msg("File pipe error", "Error creating pipe");
765 		return (pid_t)-1;
766 	}
767 
768 	if(_pipe(err_pipe, 512, O_NOINHERIT) != 0)
769 	{
770 		show_error_msg("File pipe error", "Error creating pipe");
771 		close(out_pipe[0]);
772 		close(out_pipe[1]);
773 		return (pid_t)-1;
774 	}
775 
776 	out_fd = dup(_fileno(stdout));
777 	err_fd = dup(_fileno(stderr));
778 
779 	pid = background_and_capture_internal(cmd, user_sh, out, err, out_pipe,
780 			err_pipe);
781 
782 	_close(out_pipe[1]);
783 	_close(err_pipe[1]);
784 
785 	_dup2(out_fd, _fileno(stdout));
786 	_dup2(err_fd, _fileno(stderr));
787 
788 	if(pid == (pid_t)-1)
789 	{
790 		_close(out_pipe[0]);
791 		_close(err_pipe[0]);
792 	}
793 
794 	return pid;
795 }
796 #endif
797 
798 int
bg_run_external(const char cmd[],int skip_errors,ShellRequester by)799 bg_run_external(const char cmd[], int skip_errors, ShellRequester by)
800 {
801 	bg_job_t *job = NULL;
802 #ifndef _WIN32
803 	pid_t pid;
804 	int error_pipe[2];
805 	char *command;
806 
807 	command = cfg.fast_run ? fast_run_complete(cmd) : strdup(cmd);
808 	if(command == NULL)
809 	{
810 		return -1;
811 	}
812 
813 	if(pipe(error_pipe) != 0)
814 	{
815 		show_error_msg("File pipe error", "Error creating pipe");
816 		free(command);
817 		return -1;
818 	}
819 
820 	if((pid = fork()) == -1)
821 	{
822 		free(command);
823 		return -1;
824 	}
825 
826 	if(pid == 0)
827 	{
828 		extern char **environ;
829 
830 		int nullfd;
831 		/* Redirect stderr to write end of pipe. */
832 		if(dup2(error_pipe[1], STDERR_FILENO) == -1)
833 		{
834 			perror("dup2");
835 			_Exit(EXIT_FAILURE);
836 		}
837 		close(STDIN_FILENO);
838 		close(STDOUT_FILENO);
839 		/* Close read end of pipe. */
840 		close(error_pipe[0]);
841 
842 		/* Attach stdout, stdin to /dev/null. */
843 		nullfd = open("/dev/null", O_RDWR);
844 		if(nullfd != -1)
845 		{
846 			if(dup2(nullfd, STDIN_FILENO) == -1)
847 			{
848 				perror("dup2 for stdin");
849 				_Exit(EXIT_FAILURE);
850 			}
851 			if(dup2(nullfd, STDOUT_FILENO) == -1)
852 			{
853 				perror("dup2 for stdout");
854 				_Exit(EXIT_FAILURE);
855 			}
856 		}
857 
858 		setpgid(0, 0);
859 
860 		prepare_for_exec();
861 		char *sh_flag = (by == SHELL_BY_USER ? cfg.shell_cmd_flag : "-c");
862 		execve(get_execv_path(cfg.shell),
863 				make_execv_array(cfg.shell, sh_flag, command), environ);
864 		_Exit(127);
865 	}
866 	else
867 	{
868 		/* Close write end of pipe. */
869 		close(error_pipe[1]);
870 
871 		job = add_background_job(pid, command, (uintptr_t)error_pipe[0], 0,
872 				BJT_COMMAND);
873 		if(job == NULL)
874 		{
875 			free(command);
876 			return -1;
877 		}
878 	}
879 	free(command);
880 #else
881 	BOOL ret;
882 	STARTUPINFOW startup = { .dwFlags = STARTF_USESTDHANDLES };
883 	PROCESS_INFORMATION pinfo;
884 	char *command;
885 	char *sh_cmd;
886 	wchar_t *wide_cmd;
887 
888 	command = cfg.fast_run ? fast_run_complete(cmd) : strdup(cmd);
889 	if(command == NULL)
890 	{
891 		return -1;
892 	}
893 
894 	SECURITY_ATTRIBUTES sec_attr = {
895 		.nLength = sizeof(sec_attr),
896 		.lpSecurityDescriptor = NULL,
897 		.bInheritHandle = 1,
898 	};
899 
900 	HANDLE hnul = CreateFileA("NUL", GENERIC_READ | GENERIC_WRITE, 0, &sec_attr,
901 			OPEN_EXISTING, 0, NULL);
902 	if(hnul == INVALID_HANDLE_VALUE)
903 	{
904 		free(command);
905 		return -1;
906 	}
907 	startup.hStdInput = hnul;
908 	startup.hStdOutput = hnul;
909 
910 	HANDLE herr;
911 	if(!CreatePipe(&herr, &startup.hStdError, &sec_attr, 16*1024))
912 	{
913 		CloseHandle(hnul);
914 		free(command);
915 		return -1;
916 	}
917 
918 	sh_cmd = win_make_sh_cmd(command, by);
919 	free(command);
920 
921 	wide_cmd = to_wide(sh_cmd);
922 	ret = CreateProcessW(NULL, wide_cmd, NULL, NULL, 1, 0, NULL, NULL, &startup,
923 			&pinfo);
924 	free(wide_cmd);
925 	CloseHandle(hnul);
926 	CloseHandle(startup.hStdError);
927 
928 	if(ret != 0)
929 	{
930 		CloseHandle(pinfo.hThread);
931 
932 		job = add_background_job(pinfo.dwProcessId, sh_cmd, (uintptr_t)herr,
933 				(uintptr_t)pinfo.hProcess, BJT_COMMAND);
934 		if(job == NULL)
935 		{
936 			free(sh_cmd);
937 			return -1;
938 		}
939 	}
940 	free(sh_cmd);
941 	if(ret == 0)
942 	{
943 		return 1;
944 	}
945 #endif
946 
947 	if(job != NULL)
948 	{
949 		job->skip_errors = skip_errors;
950 	}
951 	return 0;
952 }
953 
954 int
bg_execute(const char descr[],const char op_descr[],int total,int important,bg_task_func task_func,void * args)955 bg_execute(const char descr[], const char op_descr[], int total, int important,
956 		bg_task_func task_func, void *args)
957 {
958 	pthread_t id;
959 	int ret;
960 
961 	background_task_args *const task_args = malloc(sizeof(*task_args));
962 	if(task_args == NULL)
963 	{
964 		return 1;
965 	}
966 
967 	task_args->func = task_func;
968 	task_args->args = args;
969 	task_args->job = add_background_job(WRONG_PID, descr, (uintptr_t)NO_JOB_ID,
970 			(uintptr_t)NO_JOB_ID, important ? BJT_OPERATION : BJT_TASK);
971 
972 	if(task_args->job == NULL)
973 	{
974 		free(task_args);
975 		return 1;
976 	}
977 
978 	replace_string(&task_args->job->bg_op.descr, op_descr);
979 	task_args->job->bg_op.total = total;
980 
981 	if(task_args->job->type == BJT_OPERATION)
982 	{
983 		ui_stat_job_bar_add(&task_args->job->bg_op);
984 	}
985 
986 	ret = 0;
987 	if(pthread_create(&id, NULL, &background_task_bootstrap, task_args) != 0)
988 	{
989 		/* Mark job as finished with error. */
990 		pthread_spin_lock(&task_args->job->status_lock);
991 		task_args->job->running = 0;
992 		task_args->job->exit_code = 1;
993 		pthread_spin_unlock(&task_args->job->status_lock);
994 
995 		free(task_args);
996 		ret = 1;
997 	}
998 
999 	return ret;
1000 }
1001 
1002 /* Creates structure that describes background job and registers it in the list
1003  * of jobs. */
1004 static bg_job_t *
add_background_job(pid_t pid,const char cmd[],uintptr_t err,uintptr_t data,BgJobType type)1005 add_background_job(pid_t pid, const char cmd[], uintptr_t err, uintptr_t data,
1006 		BgJobType type)
1007 {
1008 	bg_job_t *new = malloc(sizeof(*new));
1009 	if(new == NULL)
1010 	{
1011 		show_error_msg("Memory error", "Unable to allocate enough memory");
1012 		return NULL;
1013 	}
1014 	new->type = type;
1015 	new->pid = pid;
1016 	new->cmd = strdup(cmd);
1017 	new->next = bg_jobs;
1018 	new->skip_errors = 0;
1019 	new->new_errors = NULL;
1020 	new->new_errors_len = 0U;
1021 	new->errors = NULL;
1022 	new->errors_len = 0U;
1023 	new->cancelled = 0;
1024 
1025 	pthread_spin_init(&new->errors_lock, PTHREAD_PROCESS_PRIVATE);
1026 	pthread_spin_init(&new->status_lock, PTHREAD_PROCESS_PRIVATE);
1027 	new->running = 1;
1028 	new->in_use = (type == BJT_COMMAND);
1029 	new->exit_code = -1;
1030 
1031 #ifndef _WIN32
1032 	new->err_stream = (int)err;
1033 #else
1034 	new->err_stream = (HANDLE)err;
1035 	new->hprocess = (HANDLE)data;
1036 #endif
1037 
1038 	if(new->err_stream != NO_JOB_ID)
1039 	{
1040 		pthread_mutex_lock(&new_err_jobs_lock);
1041 		new->err_next = new_err_jobs;
1042 		new_err_jobs = new;
1043 		pthread_mutex_unlock(&new_err_jobs_lock);
1044 		pthread_cond_signal(&new_err_jobs_cond);
1045 	}
1046 
1047 	if(type != BJT_COMMAND)
1048 	{
1049 		pthread_spin_init(&new->bg_op_lock, PTHREAD_PROCESS_PRIVATE);
1050 	}
1051 	new->bg_op.total = 0;
1052 	new->bg_op.done = 0;
1053 	new->bg_op.progress = -1;
1054 	new->bg_op.descr = NULL;
1055 	new->bg_op.cancelled = 0;
1056 
1057 	bg_jobs = new;
1058 	return new;
1059 }
1060 
1061 /* pthreads entry point for a new background task.  Performs correct
1062  * startup/exit with related updates of internal data structures.  Returns
1063  * result for this thread. */
1064 static void *
background_task_bootstrap(void * arg)1065 background_task_bootstrap(void *arg)
1066 {
1067 	background_task_args *const task_args = arg;
1068 
1069 	(void)pthread_detach(pthread_self());
1070 	block_all_thread_signals();
1071 	set_current_job(task_args->job);
1072 
1073 	task_args->func(&task_args->job->bg_op, task_args->args);
1074 
1075 	/* Mark task as finished normally. */
1076 	pthread_spin_lock(&task_args->job->status_lock);
1077 	task_args->job->running = 0;
1078 	task_args->job->exit_code = 0;
1079 	pthread_spin_unlock(&task_args->job->status_lock);
1080 
1081 	free(task_args);
1082 
1083 	return NULL;
1084 }
1085 
1086 /* Stores pointer to the job in a thread-local storage. */
1087 static void
set_current_job(bg_job_t * job)1088 set_current_job(bg_job_t *job)
1089 {
1090 	static pthread_once_t once = PTHREAD_ONCE_INIT;
1091 	pthread_once(&once, &make_current_job_key);
1092 
1093 	(void)pthread_setspecific(current_job, job);
1094 }
1095 
1096 /* current_job initializer for pthread_once(). */
1097 static void
make_current_job_key(void)1098 make_current_job_key(void)
1099 {
1100 	(void)pthread_key_create(&current_job, NULL);
1101 }
1102 
1103 int
bg_has_active_jobs(int important_only)1104 bg_has_active_jobs(int important_only)
1105 {
1106 	if(bg_jobs_freeze() != 0)
1107 	{
1108 		/* Failed to lock jobs list and using safe choice: pretend there are active
1109 		 * tasks. */
1110 		return 1;
1111 	}
1112 
1113 	bg_job_t *job;
1114 	int running = 0;
1115 	for(job = bg_jobs; job != NULL && !running; job = job->next)
1116 	{
1117 		if((important_only && job->type == BJT_OPERATION) ||
1118 				(!important_only && job->type != BJT_COMMAND))
1119 		{
1120 			running |= bg_job_is_running(job);
1121 		}
1122 	}
1123 
1124 	bg_jobs_unfreeze();
1125 
1126 	return running;
1127 }
1128 
1129 int
bg_jobs_freeze(void)1130 bg_jobs_freeze(void)
1131 {
1132 	/* SIGCHLD needs to be blocked anytime the jobs list is accessed from anywhere
1133 	 * except the received_sigchld(). */
1134 	return set_sigchld(1);
1135 }
1136 
1137 void
bg_jobs_unfreeze(void)1138 bg_jobs_unfreeze(void)
1139 {
1140 	/* Unblock SIGCHLD signal. */
1141 	/* FIXME: maybe store previous state of SIGCHLD and don't unblock if it was
1142 	 *        blocked. */
1143 	(void)set_sigchld(0);
1144 }
1145 
1146 int
bg_job_cancel(bg_job_t * job)1147 bg_job_cancel(bg_job_t *job)
1148 {
1149 	int was_cancelled;
1150 
1151 	if(job->type != BJT_COMMAND)
1152 	{
1153 		return !bg_op_cancel(&job->bg_op);
1154 	}
1155 
1156 	was_cancelled = job->cancelled;
1157 #ifndef _WIN32
1158 	if(kill(job->pid, SIGINT) == 0)
1159 	{
1160 		job->cancelled = 1;
1161 	}
1162 	else
1163 	{
1164 		LOG_SERROR_MSG(errno, "Failed to send SIGINT to %" PRINTF_ULL,
1165 				(unsigned long long)job->pid);
1166 	}
1167 #else
1168 	if(win_cancel_process(job->pid, job->hprocess) == 0)
1169 	{
1170 		job->cancelled = 1;
1171 	}
1172 	else
1173 	{
1174 		LOG_SERROR_MSG(errno, "Failed to send WM_CLOSE to %" PRINTF_ULL,
1175 				(unsigned long long)job->pid);
1176 	}
1177 #endif
1178 	return !was_cancelled;
1179 }
1180 
1181 int
bg_job_cancelled(bg_job_t * job)1182 bg_job_cancelled(bg_job_t *job)
1183 {
1184 	if(job->type != BJT_COMMAND)
1185 	{
1186 		return bg_op_cancelled(&job->bg_op);
1187 	}
1188 	return job->cancelled;
1189 }
1190 
1191 int
bg_job_is_running(bg_job_t * job)1192 bg_job_is_running(bg_job_t *job)
1193 {
1194 	int running;
1195 	pthread_spin_lock(&job->status_lock);
1196 	running = job->running;
1197 	pthread_spin_unlock(&job->status_lock);
1198 	return running;
1199 }
1200 
1201 void
bg_op_lock(bg_op_t * bg_op)1202 bg_op_lock(bg_op_t *bg_op)
1203 {
1204 	bg_job_t *const job = STRUCT_FROM_FIELD(bg_job_t, bg_op, bg_op);
1205 	pthread_spin_lock(&job->bg_op_lock);
1206 }
1207 
1208 void
bg_op_unlock(bg_op_t * bg_op)1209 bg_op_unlock(bg_op_t *bg_op)
1210 {
1211 	bg_job_t *const job = STRUCT_FROM_FIELD(bg_job_t, bg_op, bg_op);
1212 	pthread_spin_unlock(&job->bg_op_lock);
1213 }
1214 
1215 void
bg_op_changed(bg_op_t * bg_op)1216 bg_op_changed(bg_op_t *bg_op)
1217 {
1218 	ui_stat_job_bar_changed(bg_op);
1219 }
1220 
1221 void
bg_op_set_descr(bg_op_t * bg_op,const char descr[])1222 bg_op_set_descr(bg_op_t *bg_op, const char descr[])
1223 {
1224 	bg_op_lock(bg_op);
1225 	replace_string(&bg_op->descr, descr);
1226 	bg_op_unlock(bg_op);
1227 
1228 	bg_op_changed(bg_op);
1229 }
1230 
1231 /* Convenience method to cancel background job.  Returns previous version of the
1232  * cancellation flag. */
1233 static int
bg_op_cancel(bg_op_t * bg_op)1234 bg_op_cancel(bg_op_t *bg_op)
1235 {
1236 	int was_cancelled;
1237 
1238 	bg_op_lock(bg_op);
1239 	was_cancelled = bg_op->cancelled;
1240 	bg_op->cancelled = 1;
1241 	bg_op_unlock(bg_op);
1242 
1243 	bg_op_changed(bg_op);
1244 	return was_cancelled;
1245 }
1246 
1247 int
bg_op_cancelled(bg_op_t * bg_op)1248 bg_op_cancelled(bg_op_t *bg_op)
1249 {
1250 	int cancelled;
1251 
1252 	bg_op_lock(bg_op);
1253 	cancelled = bg_op->cancelled;
1254 	bg_op_unlock(bg_op);
1255 
1256 	return cancelled;
1257 }
1258 
1259 /* vim: set tabstop=2 softtabstop=2 shiftwidth=2 noexpandtab cinoptions-=(0 : */
1260 /* vim: set cinoptions+=t0 filetype=c : */
1261