1 /* actions.c -- a few functions for the plugins systems */
2
3 /*
4 * This file is part of CliFM
5 *
6 * Copyright (C) 2016-2021, L. Abramovich <johndoe.arch@outlook.com>
7 * All rights reserved.
8
9 * CliFM is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * CliFM is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
22 * MA 02110-1301, USA.
23 */
24
25 #include "helpers.h"
26
27 #include <errno.h>
28 #include <fcntl.h>
29 #include <stdio.h>
30 #include <string.h>
31 #include <sys/stat.h>
32 #include <sys/wait.h>
33 #include <unistd.h>
34
35 #include "aux.h"
36 #include "checks.h"
37 #include "exec.h"
38 #include "file_operations.h"
39 #include "init.h"
40 #include "mime.h"
41 #include "misc.h"
42
43 /* The core of this function was taken from NNN's run_selected_plugin
44 * function and modified to fit our needs. Thanks NNN! */
45 int
run_action(char * action,char ** args)46 run_action(char *action, char **args)
47 {
48 if (!action)
49 return EXIT_FAILURE;
50
51 int exit_status = EXIT_SUCCESS;
52 char *cmd = (char *)NULL;
53 size_t len = 0,
54 action_len = strlen(action);
55
56 /* #####################################
57 * # 1) CREATE CMD TO BE EXECUTED #
58 * ##################################### */
59
60 /* Remove terminating new line char */
61 if (action[action_len - 1] == '\n')
62 action[action_len - 1] = '\0';
63
64 int dir_path = 0;
65 if (strchr(action, '/')) {
66 cmd = (char *)xnmalloc(action_len + 1, sizeof(char));
67 strcpy(cmd, action);
68 dir_path = 1;
69 } else { /* If not a path, PLUGINS_DIR is assumed */
70 if (!plugins_dir || !*plugins_dir) {
71 fprintf(stderr, "%s: Plugins directory not defined\n", PROGRAM_NAME);
72 return EXIT_FAILURE;
73 }
74 cmd = (char *)xnmalloc(action_len + strlen(plugins_dir) + 2,
75 sizeof(char));
76 sprintf(cmd, "%s/%s", plugins_dir, action);
77 }
78
79 /* Check if the action file exists and is executable */
80 if (access(cmd, X_OK) == -1) {
81 /* If not in local dir, check system data dir as well */
82 if (data_dir && !dir_path) {
83 cmd = (char *)xrealloc(cmd, (action_len + strlen(data_dir)
84 + strlen(PNL) + 11) * sizeof(char));
85 sprintf(cmd, "%s/%s/plugins/%s", data_dir, PNL, action);
86 if (access(cmd, X_OK) == -1) {
87 fprintf(stderr, "actions: %s: %s\n", cmd, strerror(errno));
88 free(cmd);
89 return EXIT_FAILURE;
90 }
91 } else {
92 fprintf(stderr, "actions: %s: %s\n", cmd, strerror(errno));
93 free(cmd);
94 return EXIT_FAILURE;
95 }
96 }
97
98 /* Append arguments to command */
99 /* Arguments are already escaped */
100 size_t i;
101 len = strlen(cmd);
102 for (i = 1; args[i]; i++) {
103 len += (strlen(args[i]) + 2);
104 cmd = (char *)xrealloc(cmd, len * sizeof(char));
105 strcat(cmd, " ");
106 strcat(cmd, args[i]);
107 }
108
109 /* ##############################
110 * # 2) CREATE A PIPE FILE #
111 * ############################## */
112
113 char *rand_ext = gen_rand_str(6);
114 if (!rand_ext) {
115 free(cmd);
116 return EXIT_FAILURE;
117 }
118
119 char fifo_path[PATH_MAX];
120 sprintf(fifo_path, "%s/.pipe.%s", tmp_dir, rand_ext);
121 free(rand_ext);
122
123 setenv("CLIFM_BUS", fifo_path, 1);
124
125 if (mkfifo(fifo_path, 0600) != EXIT_SUCCESS) {
126 free(cmd);
127 printf("%s: %s\n", fifo_path, strerror(errno));
128 return EXIT_FAILURE;
129 }
130
131 /* ################################################
132 * # 3) EXEC CMD & LET THE CHILD WRITE TO PIPE #
133 * ################################################ */
134
135 /* Set terminal title to plugin name */
136 if (xargs.cwd_in_title == 1)
137 set_term_title(action);
138
139 pid_t pid = fork();
140
141 if (pid == 0) {
142 /* Child: write-only end of the pipe */
143 int wfd = open(fifo_path, O_WRONLY | O_CLOEXEC);
144
145 if (wfd == -1)
146 _exit(EXIT_FAILURE);
147
148 launch_execle(cmd);
149 close(wfd);
150 _exit(EXIT_SUCCESS);
151 }
152
153 free(cmd);
154
155 /* ########################################
156 * # 4) LET THE PARENT READ THE PIPE #
157 * ######################################## */
158
159 /* Parent: read-only end of the pipe */
160 int rfd;
161
162 do
163 rfd = open(fifo_path, O_RDONLY);
164 while (rfd == -1 && errno == EINTR);
165
166 char buf[PATH_MAX] = "";
167 ssize_t buf_len = 0;
168
169 do
170 buf_len = read(rfd, buf, sizeof(buf));
171 while (buf_len == -1 && errno == EINTR);
172
173 close(rfd);
174
175 /* Wait for the child to finish. Otherwise, the child is left as
176 * zombie process */
177 int status = 0;
178 waitpid(pid, &status, 0);
179
180 /* If the pipe is empty */
181 if (!*buf) {
182 unlink(fifo_path);
183 if (xargs.cwd_in_title == 1)
184 set_term_title(ws[cur_ws].path);
185 return EXIT_SUCCESS;
186 }
187
188 if (buf[buf_len - 1] == '\n')
189 buf[buf_len - 1] = '\0';
190
191 /* If a valid file */
192 struct stat attr;
193
194 if (lstat(buf, &attr) != -1) {
195 char *o_cmd[] = {"o", buf, NULL};
196 exit_status = open_function(o_cmd);
197 } else { /* If not a file, take it as a command*/
198 size_t old_args = args_n;
199 args_n = 0;
200
201 char **_cmd = parse_input_str(buf);
202 if (_cmd) {
203 char **alias_cmd = check_for_alias(_cmd);
204
205 if (alias_cmd) {
206 exit_status = exec_cmd(alias_cmd);
207 for (i = 0; alias_cmd[i]; i++)
208 free(alias_cmd[i]);
209 free(alias_cmd);
210 } else {
211 exit_status = exec_cmd(_cmd);
212 for (i = 0; i <= args_n; i++)
213 free(_cmd[i]);
214 free(_cmd);
215 }
216 }
217
218 args_n = old_args;
219 }
220
221 /* Remove the pipe file */
222 unlink(fifo_path);
223
224 if (xargs.cwd_in_title == 1)
225 set_term_title(ws[cur_ws].path);
226
227 return exit_status;
228 }
229
230 int
edit_actions(void)231 edit_actions(void)
232 {
233 if (xargs.stealth_mode == 1) {
234 printf("%s: Access to configuration files is not allowed in "
235 "stealth mode\n", PROGRAM_NAME);
236 return EXIT_SUCCESS;
237 }
238
239 /* Get actions file's current modification time */
240 struct stat file_attrib;
241
242 if (stat(actions_file, &file_attrib) == -1) {
243 fprintf(stderr, "actions: %s: %s\n", actions_file, strerror(errno));
244 return EXIT_FAILURE;
245 }
246
247 time_t mtime_bfr = (time_t)file_attrib.st_mtime;
248
249 int ret = open_file(actions_file);
250 if (ret != EXIT_SUCCESS)
251 return EXIT_FAILURE;
252
253 /* Get modification time after opening the file */
254 stat(actions_file, &file_attrib);
255
256 /* If modification times differ, the file was modified after being
257 * opened */
258 if (mtime_bfr != (time_t)file_attrib.st_mtime) {
259 /* Reload the array of available actions */
260 if (load_actions() != EXIT_SUCCESS)
261 return EXIT_FAILURE;
262
263 /* Reload PATH commands as well to add new action(s) */
264 if (bin_commands) {
265 size_t i;
266 for (i = 0; bin_commands[i]; i++)
267 free(bin_commands[i]);
268
269 free(bin_commands);
270 bin_commands = (char **)NULL;
271 }
272
273 if (paths) {
274 size_t i;
275 for (i = 0; i < path_n; i++)
276 free(paths[i]);
277 }
278
279 path_n = (size_t)get_path_env();
280 get_path_programs();
281 }
282
283 return EXIT_SUCCESS;
284 }
285