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(¤t_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