query available/used device speeds via ethtool
[project/netifd.git] / proto-shell.c
1 #define _GNU_SOURCE
2
3 #include <string.h>
4 #include <stdlib.h>
5 #include <stdio.h>
6 #include <glob.h>
7 #include <unistd.h>
8 #include <fcntl.h>
9 #include <signal.h>
10
11 #include <arpa/inet.h>
12 #include <netinet/in.h>
13
14 #include <libubox/blobmsg_json.h>
15
16 #include "netifd.h"
17 #include "interface.h"
18 #include "interface-ip.h"
19 #include "proto.h"
20
21 static struct netifd_fd proto_fd;
22
23 enum proto_shell_sm {
24         S_IDLE,
25         S_SETUP,
26         S_SETUP_ABORT,
27         S_TEARDOWN,
28 };
29
30 struct proto_shell_handler {
31         struct list_head list;
32         struct proto_handler proto;
33         struct config_param_list config;
34         char *config_buf;
35         bool init_available;
36         char script_name[];
37 };
38
39 struct proto_shell_state {
40         struct interface_proto_state proto;
41         struct proto_shell_handler *handler;
42         struct blob_attr *config;
43
44         struct device_user l3_dev;
45
46         struct uloop_timeout teardown_timeout;
47
48         struct netifd_process setup_task;
49         struct netifd_process teardown_task;
50         struct netifd_process proto_task;
51
52         enum proto_shell_sm sm;
53         bool proto_task_killed;
54
55         int last_error;
56 };
57
58 static int
59 proto_shell_handler(struct interface_proto_state *proto,
60                     enum interface_proto_cmd cmd, bool force)
61 {
62         struct proto_shell_state *state;
63         struct proto_shell_handler *handler;
64         struct netifd_process *proc;
65         static char error_buf[32];
66         const char *argv[7];
67         char *envp[2];
68         const char *action;
69         char *config;
70         int ret, i = 0, j = 0;
71
72         state = container_of(proto, struct proto_shell_state, proto);
73         handler = state->handler;
74
75         if (cmd == PROTO_CMD_SETUP) {
76                 action = "setup";
77                 proc = &state->setup_task;
78                 state->last_error = -1;
79         } else {
80                 if (state->sm == S_TEARDOWN)
81                         return 0;
82
83                 if (state->setup_task.uloop.pending) {
84                         if (state->sm != S_SETUP_ABORT) {
85                                 uloop_timeout_set(&state->teardown_timeout, 1000);
86                                 kill(state->setup_task.uloop.pid, SIGTERM);
87                                 if (state->proto_task.uloop.pending)
88                                         kill(state->proto_task.uloop.pid, SIGTERM);
89                                 state->sm = S_SETUP_ABORT;
90                         }
91                         return 0;
92                 }
93
94                 action = "teardown";
95                 proc = &state->teardown_task;
96                 state->sm = S_TEARDOWN;
97                 if (state->last_error >= 0) {
98                         snprintf(error_buf, sizeof(error_buf), "ERROR=%d", state->last_error);
99                         envp[j++] = error_buf;
100                 }
101                 uloop_timeout_set(&state->teardown_timeout, 5000);
102         }
103
104         config = blobmsg_format_json(state->config, true);
105         if (!config)
106                 return -1;
107
108         argv[i++] = handler->script_name;
109         argv[i++] = handler->proto.name;
110         argv[i++] = action;
111         argv[i++] = proto->iface->name;
112         argv[i++] = config;
113         if (proto->iface->main_dev.dev)
114                 argv[i++] = proto->iface->main_dev.dev->ifname;
115         argv[i] = NULL;
116         envp[j] = NULL;
117
118         ret = netifd_start_process(argv, envp, proc);
119         free(config);
120
121         return ret;
122 }
123
124 static void
125 proto_shell_task_finish(struct proto_shell_state *state,
126                         struct netifd_process *task)
127 {
128         switch (state->sm) {
129         case S_IDLE:
130                 if (task == &state->proto_task)
131                         state->proto.proto_event(&state->proto, IFPEV_LINK_LOST);
132                 /* fall through */
133         case S_SETUP:
134                 if (task == &state->proto_task)
135                         proto_shell_handler(&state->proto, PROTO_CMD_TEARDOWN,
136                                             false);
137                 break;
138
139         case S_SETUP_ABORT:
140                 if (state->setup_task.uloop.pending ||
141                     state->proto_task.uloop.pending)
142                         break;
143
144                 uloop_timeout_cancel(&state->teardown_timeout);
145                 state->sm = S_IDLE;
146                 proto_shell_handler(&state->proto, PROTO_CMD_TEARDOWN, false);
147                 break;
148
149         case S_TEARDOWN:
150                 if (state->teardown_task.uloop.pending)
151                         break;
152
153                 if (state->proto_task.uloop.pending) {
154                         if (!state->proto_task_killed)
155                                 kill(state->proto_task.uloop.pid, SIGTERM);
156                         break;
157                 }
158
159                 uloop_timeout_cancel(&state->teardown_timeout);
160                 state->sm = S_IDLE;
161                 state->proto.proto_event(&state->proto, IFPEV_DOWN);
162                 break;
163         }
164 }
165
166 static void
167 proto_shell_teardown_timeout_cb(struct uloop_timeout *timeout)
168 {
169         struct proto_shell_state *state;
170
171         state = container_of(timeout, struct proto_shell_state, teardown_timeout);
172
173         netifd_kill_process(&state->setup_task);
174         netifd_kill_process(&state->proto_task);
175         netifd_kill_process(&state->teardown_task);
176         proto_shell_task_finish(state, NULL);
177 }
178
179 static void
180 proto_shell_setup_cb(struct netifd_process *p, int ret)
181 {
182         struct proto_shell_state *state;
183
184         state = container_of(p, struct proto_shell_state, setup_task);
185         proto_shell_task_finish(state, p);
186 }
187
188 static void
189 proto_shell_teardown_cb(struct netifd_process *p, int ret)
190 {
191         struct proto_shell_state *state;
192
193         state = container_of(p, struct proto_shell_state, teardown_task);
194         proto_shell_task_finish(state, p);
195 }
196
197 static void
198 proto_shell_task_cb(struct netifd_process *p, int ret)
199 {
200         struct proto_shell_state *state;
201
202         state = container_of(p, struct proto_shell_state, proto_task);
203
204         if (state->sm == S_IDLE || state->sm == S_SETUP)
205                 state->last_error = WEXITSTATUS(ret);
206
207         proto_shell_task_finish(state, p);
208 }
209
210 static void
211 proto_shell_free(struct interface_proto_state *proto)
212 {
213         struct proto_shell_state *state;
214
215         state = container_of(proto, struct proto_shell_state, proto);
216         free(state->config);
217         free(state);
218 }
219
220 static void
221 proto_shell_parse_addr_list(struct interface_ip_settings *ip, struct blob_attr *attr,
222                             bool v6, bool external)
223 {
224         struct device_addr *addr;
225         struct blob_attr *cur;
226         int rem;
227
228         blobmsg_for_each_attr(cur, attr, rem) {
229                 if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING) {
230                         DPRINTF("Ignore wrong address type: %d\n", blobmsg_type(cur));
231                         continue;
232                 }
233
234                 addr = proto_parse_ip_addr_string(blobmsg_data(cur), v6, v6 ? 32 : 128);
235                 if (!addr) {
236                         DPRINTF("Failed to parse IP address string: %s\n", (char *) blobmsg_data(cur));
237                         continue;
238                 }
239
240                 if (external)
241                         addr->flags |= DEVADDR_EXTERNAL;
242
243                 vlist_add(&ip->addr, &addr->node);
244         }
245 }
246
247 static void
248 proto_shell_parse_route_list(struct interface *iface, struct blob_attr *attr,
249                              bool v6)
250 {
251         struct blob_attr *cur;
252         int rem;
253
254         blobmsg_for_each_attr(cur, attr, rem) {
255                 if (blobmsg_type(cur) != BLOBMSG_TYPE_TABLE) {
256                         DPRINTF("Ignore wrong route type: %d\n", blobmsg_type(cur));
257                         continue;
258                 }
259
260                 interface_ip_add_route(iface, cur, v6);
261         }
262 }
263
264 enum {
265         NOTIFY_ACTION,
266         NOTIFY_ERROR,
267         NOTIFY_COMMAND,
268         NOTIFY_ENV,
269         NOTIFY_SIGNAL,
270         NOTIFY_AVAILABLE,
271         NOTIFY_LINK_UP,
272         NOTIFY_IFNAME,
273         NOTIFY_ADDR_EXT,
274         NOTIFY_IPADDR,
275         NOTIFY_IP6ADDR,
276         NOTIFY_ROUTES,
277         NOTIFY_ROUTES6,
278         NOTIFY_DNS,
279         NOTIFY_DNS_SEARCH,
280         __NOTIFY_LAST
281 };
282
283 static const struct blobmsg_policy notify_attr[__NOTIFY_LAST] = {
284         [NOTIFY_ACTION] = { .name = "action", .type = BLOBMSG_TYPE_INT32 },
285         [NOTIFY_ERROR] = { .name = "error", .type = BLOBMSG_TYPE_ARRAY },
286         [NOTIFY_COMMAND] = { .name = "command", .type = BLOBMSG_TYPE_ARRAY },
287         [NOTIFY_ENV] = { .name = "env", .type = BLOBMSG_TYPE_ARRAY },
288         [NOTIFY_SIGNAL] = { .name = "signal", .type = BLOBMSG_TYPE_INT32 },
289         [NOTIFY_AVAILABLE] = { .name = "available", .type = BLOBMSG_TYPE_BOOL },
290         [NOTIFY_LINK_UP] = { .name = "link-up", .type = BLOBMSG_TYPE_BOOL },
291         [NOTIFY_IFNAME] = { .name = "ifname", .type = BLOBMSG_TYPE_STRING },
292         [NOTIFY_ADDR_EXT] = { .name = "address-external", .type = BLOBMSG_TYPE_BOOL },
293         [NOTIFY_IPADDR] = { .name = "ipaddr", .type = BLOBMSG_TYPE_ARRAY },
294         [NOTIFY_IP6ADDR] = { .name = "ip6addr", .type = BLOBMSG_TYPE_ARRAY },
295         [NOTIFY_ROUTES] = { .name = "routes", .type = BLOBMSG_TYPE_ARRAY },
296         [NOTIFY_ROUTES6] = { .name = "routes6", .type = BLOBMSG_TYPE_ARRAY },
297         [NOTIFY_DNS] = { .name = "dns", .type = BLOBMSG_TYPE_ARRAY },
298         [NOTIFY_DNS_SEARCH] = { .name = "dns_search", .type = BLOBMSG_TYPE_ARRAY },
299 };
300
301 static int
302 proto_shell_update_link(struct proto_shell_state *state, struct blob_attr **tb)
303 {
304         struct interface_ip_settings *ip;
305         struct blob_attr *cur;
306         int dev_create = 1;
307         bool addr_ext = false;
308         bool up;
309
310         if (!tb[NOTIFY_LINK_UP])
311                 return UBUS_STATUS_INVALID_ARGUMENT;
312
313         up = blobmsg_get_bool(tb[NOTIFY_LINK_UP]);
314         if (!up) {
315                 state->proto.proto_event(&state->proto, IFPEV_LINK_LOST);
316                 return 0;
317         }
318
319         if ((cur = tb[NOTIFY_ADDR_EXT]) != NULL) {
320                 addr_ext = blobmsg_get_bool(cur);
321                 if (addr_ext)
322                         dev_create = 2;
323         }
324
325         if (!tb[NOTIFY_IFNAME]) {
326                 if (!state->proto.iface->main_dev.dev)
327                         return UBUS_STATUS_INVALID_ARGUMENT;
328         } else {
329                 if (state->l3_dev.dev)
330                         device_remove_user(&state->l3_dev);
331
332                 device_add_user(&state->l3_dev,
333                         device_get(blobmsg_data(tb[NOTIFY_IFNAME]), dev_create));
334                 state->proto.iface->l3_dev = &state->l3_dev;
335                 device_claim(&state->l3_dev);
336         }
337
338         ip = &state->proto.iface->proto_ip;
339         interface_update_start(state->proto.iface);
340
341         if ((cur = tb[NOTIFY_IPADDR]) != NULL)
342                 proto_shell_parse_addr_list(ip, cur, false, addr_ext);
343
344         if ((cur = tb[NOTIFY_IP6ADDR]) != NULL)
345                 proto_shell_parse_addr_list(ip, cur, true, addr_ext);
346
347         if ((cur = tb[NOTIFY_ROUTES]) != NULL)
348                 proto_shell_parse_route_list(state->proto.iface, cur, false);
349
350         if ((cur = tb[NOTIFY_ROUTES6]) != NULL)
351                 proto_shell_parse_route_list(state->proto.iface, cur, true);
352
353         if ((cur = tb[NOTIFY_DNS]) != NULL)
354                 interface_add_dns_server_list(ip, cur);
355
356         if ((cur = tb[NOTIFY_DNS_SEARCH]) != NULL)
357                 interface_add_dns_search_list(ip, cur);
358
359         interface_update_complete(state->proto.iface);
360
361         state->proto.proto_event(&state->proto, IFPEV_UP);
362
363         return 0;
364 }
365
366 static bool
367 fill_string_list(struct blob_attr *attr, char **argv, int max)
368 {
369         struct blob_attr *cur;
370         int argc = 0;
371         int rem;
372
373         if (!attr)
374                 goto out;
375
376         blobmsg_for_each_attr(cur, attr, rem) {
377                 if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING)
378                         return false;
379
380                 if (!blobmsg_check_attr(cur, NULL))
381                         return false;
382
383                 argv[argc++] = blobmsg_data(cur);
384                 if (argc == max - 1)
385                         return false;
386         }
387
388 out:
389         argv[argc] = NULL;
390         return true;
391 }
392
393 static int
394 proto_shell_run_command(struct proto_shell_state *state, struct blob_attr **tb)
395 {
396         static char *argv[64];
397         static char *env[32];
398
399         if (!tb[NOTIFY_COMMAND])
400                 goto error;
401
402         if (!fill_string_list(tb[NOTIFY_COMMAND], argv, ARRAY_SIZE(argv)))
403                 goto error;
404
405         if (!fill_string_list(tb[NOTIFY_ENV], env, ARRAY_SIZE(env)))
406                 goto error;
407
408         netifd_start_process((const char **) argv, (char **) env, &state->proto_task);
409
410         return 0;
411
412 error:
413         return UBUS_STATUS_INVALID_ARGUMENT;
414 }
415
416 static int
417 proto_shell_kill_command(struct proto_shell_state *state, struct blob_attr **tb)
418 {
419         unsigned int signal = ~0;
420
421         if (tb[NOTIFY_SIGNAL])
422                 signal = blobmsg_get_u32(tb[NOTIFY_SIGNAL]);
423
424         if (signal > 31)
425                 signal = SIGTERM;
426
427         if (state->proto_task.uloop.pending) {
428                 state->proto_task_killed = true;
429                 kill(state->proto_task.uloop.pid, signal);
430         }
431
432         return 0;
433 }
434
435 static int
436 proto_shell_notify_error(struct proto_shell_state *state, struct blob_attr **tb)
437 {
438         struct blob_attr *cur;
439         char *data[16];
440         int n_data = 0;
441         int rem;
442
443         if (!tb[NOTIFY_ERROR])
444                 return UBUS_STATUS_INVALID_ARGUMENT;
445
446         blobmsg_for_each_attr(cur, tb[NOTIFY_ERROR], rem) {
447                 if (n_data + 1 == ARRAY_SIZE(data))
448                         goto error;
449
450                 if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING)
451                         goto error;
452
453                 if (!blobmsg_check_attr(cur, NULL))
454                         goto error;
455
456                 data[n_data++] = blobmsg_data(cur);
457         }
458
459         if (!n_data)
460                 goto error;
461
462         interface_add_error(state->proto.iface, state->handler->proto.name,
463                         data[0], (const char **) &data[1], n_data - 1);
464
465         return 0;
466
467 error:
468         return UBUS_STATUS_INVALID_ARGUMENT;
469 }
470
471 static int
472 proto_shell_block_restart(struct proto_shell_state *state, struct blob_attr **tb)
473 {
474         state->proto.iface->autostart = false;
475         return 0;
476 }
477
478 static int
479 proto_shell_set_available(struct proto_shell_state *state, struct blob_attr **tb)
480 {
481         if (!tb[NOTIFY_AVAILABLE])
482                 return UBUS_STATUS_INVALID_ARGUMENT;
483
484         interface_set_available(state->proto.iface, blobmsg_get_bool(tb[NOTIFY_AVAILABLE]));
485         return 0;
486 }
487
488 static int
489 proto_shell_notify(struct interface_proto_state *proto, struct blob_attr *attr)
490 {
491         struct proto_shell_state *state;
492         struct blob_attr *tb[__NOTIFY_LAST];
493
494         state = container_of(proto, struct proto_shell_state, proto);
495
496         blobmsg_parse(notify_attr, __NOTIFY_LAST, tb, blob_data(attr), blob_len(attr));
497         if (!tb[NOTIFY_ACTION])
498                 return UBUS_STATUS_INVALID_ARGUMENT;
499
500         switch(blobmsg_get_u32(tb[NOTIFY_ACTION])) {
501         case 0:
502                 return proto_shell_update_link(state, tb);
503         case 1:
504                 return proto_shell_run_command(state, tb);
505         case 2:
506                 return proto_shell_kill_command(state, tb);
507         case 3:
508                 return proto_shell_notify_error(state, tb);
509         case 4:
510                 return proto_shell_block_restart(state, tb);
511         case 5:
512                 return proto_shell_set_available(state, tb);
513         default:
514                 return UBUS_STATUS_INVALID_ARGUMENT;
515         }
516 }
517
518 static struct interface_proto_state *
519 proto_shell_attach(const struct proto_handler *h, struct interface *iface,
520                    struct blob_attr *attr)
521 {
522         struct proto_shell_state *state;
523
524         state = calloc(1, sizeof(*state));
525         state->config = malloc(blob_pad_len(attr));
526         if (!state->config)
527                 goto error;
528
529         memcpy(state->config, attr, blob_pad_len(attr));
530         state->proto.free = proto_shell_free;
531         state->proto.notify = proto_shell_notify;
532         state->proto.cb = proto_shell_handler;
533         state->teardown_timeout.cb = proto_shell_teardown_timeout_cb;
534         state->setup_task.cb = proto_shell_setup_cb;
535         state->setup_task.dir_fd = proto_fd.fd;
536         state->setup_task.log_prefix = iface->name;
537         state->teardown_task.cb = proto_shell_teardown_cb;
538         state->teardown_task.dir_fd = proto_fd.fd;
539         state->teardown_task.log_prefix = iface->name;
540         state->proto_task.cb = proto_shell_task_cb;
541         state->proto_task.dir_fd = proto_fd.fd;
542         state->proto_task.log_prefix = iface->name;
543         state->handler = container_of(h, struct proto_shell_handler, proto);
544
545         return &state->proto;
546
547 error:
548         free(state);
549         return NULL;
550 }
551
552 static json_object *
553 check_type(json_object *obj, json_type type)
554 {
555         if (!obj)
556                 return NULL;
557
558         if (json_object_get_type(obj) != type)
559                 return NULL;
560
561         return obj;
562 }
563
564 static inline json_object *
565 get_field(json_object *obj, const char *name, json_type type)
566 {
567         return check_type(json_object_object_get(obj, name), type);
568 }
569
570 static char *
571 proto_shell_parse_config(struct config_param_list *config, json_object *obj)
572 {
573         struct blobmsg_policy *attrs;
574         char *str_buf, *str_cur;
575         int str_len = 0;
576         int i;
577
578         config->n_params = json_object_array_length(obj);
579         attrs = calloc(1, sizeof(*attrs) * config->n_params);
580         if (!attrs)
581                 return NULL;
582
583         config->params = attrs;
584         for (i = 0; i < config->n_params; i++) {
585                 json_object *cur, *name, *type;
586
587                 cur = check_type(json_object_array_get_idx(obj, i), json_type_array);
588                 if (!cur)
589                         goto error;
590
591                 name = check_type(json_object_array_get_idx(cur, 0), json_type_string);
592                 if (!name)
593                         goto error;
594
595                 type = check_type(json_object_array_get_idx(cur, 1), json_type_int);
596                 if (!type)
597                         goto error;
598
599                 attrs[i].name = json_object_get_string(name);
600                 attrs[i].type = json_object_get_int(type);
601                 if (attrs[i].type > BLOBMSG_TYPE_LAST)
602                         goto error;
603
604                 str_len += strlen(attrs[i].name) + 1;
605         }
606
607         str_buf = malloc(str_len);
608         if (!str_buf)
609                 goto error;
610
611         str_cur = str_buf;
612         for (i = 0; i < config->n_params; i++) {
613                 const char *name = attrs[i].name;
614
615                 attrs[i].name = str_cur;
616                 str_cur += sprintf(str_cur, "%s", name) + 1;
617         }
618
619         return str_buf;
620
621 error:
622         free(attrs);
623         config->n_params = 0;
624         return NULL;
625 }
626
627 static void
628 proto_shell_add_handler(const char *script, json_object *obj)
629 {
630         struct proto_shell_handler *handler;
631         struct proto_handler *proto;
632         json_object *config, *tmp;
633         const char *name;
634         char *str;
635
636         if (!check_type(obj, json_type_object))
637                 return;
638
639         tmp = get_field(obj, "name", json_type_string);
640         if (!tmp)
641                 return;
642
643         name = json_object_get_string(tmp);
644
645         handler = calloc(1, sizeof(*handler) +
646                          strlen(script) + 1 +
647                          strlen(name) + 1);
648         if (!handler)
649                 return;
650
651         strcpy(handler->script_name, script);
652
653         str = handler->script_name + strlen(handler->script_name) + 1;
654         strcpy(str, name);
655
656         proto = &handler->proto;
657         proto->name = str;
658         proto->config_params = &handler->config;
659         proto->attach = proto_shell_attach;
660
661         tmp = get_field(obj, "no-device", json_type_boolean);
662         if (tmp && json_object_get_boolean(tmp))
663                 handler->proto.flags |= PROTO_FLAG_NODEV;
664
665         tmp = get_field(obj, "available", json_type_boolean);
666         if (tmp && json_object_get_boolean(tmp))
667                 handler->proto.flags |= PROTO_FLAG_INIT_AVAILABLE;
668
669         config = get_field(obj, "config", json_type_array);
670         if (config)
671                 handler->config_buf = proto_shell_parse_config(&handler->config, config);
672
673         DPRINTF("Add handler for script %s: %s\n", script, proto->name);
674         add_proto_handler(proto);
675 }
676
677 static void proto_shell_add_script(const char *name)
678 {
679         struct json_tokener *tok = NULL;
680         json_object *obj;
681         static char buf[512];
682         char *start, *cmd;
683         FILE *f;
684         int len;
685
686 #define DUMP_SUFFIX     " '' dump"
687
688         cmd = alloca(strlen(name) + 1 + sizeof(DUMP_SUFFIX));
689         sprintf(cmd, "%s" DUMP_SUFFIX, name);
690
691         f = popen(cmd, "r");
692         if (!f)
693                 return;
694
695         do {
696                 start = fgets(buf, sizeof(buf), f);
697                 if (!start)
698                         continue;
699
700                 len = strlen(start);
701
702                 if (!tok)
703                         tok = json_tokener_new();
704
705                 obj = json_tokener_parse_ex(tok, start, len);
706                 if (!is_error(obj)) {
707                         proto_shell_add_handler(name, obj);
708                         json_object_put(obj);
709                         json_tokener_free(tok);
710                         tok = NULL;
711                 } else if (start[len - 1] == '\n') {
712                         json_tokener_free(tok);
713                         tok = NULL;
714                 }
715         } while (!feof(f) && !ferror(f));
716
717         if (tok)
718                 json_tokener_free(tok);
719
720         pclose(f);
721 }
722
723 static void __init proto_shell_init(void)
724 {
725         glob_t g;
726         int main_fd;
727         int i;
728
729         main_fd = open(".", O_RDONLY | O_DIRECTORY);
730         if (main_fd < 0)
731                 return;
732
733         if (chdir(main_path)) {
734                 perror("chdir(main path)");
735                 goto close_cur;
736         }
737
738         if (chdir("./proto"))
739                 goto close_cur;
740
741         proto_fd.fd = open(".", O_RDONLY | O_DIRECTORY);
742         if (proto_fd.fd < 0)
743                 goto close_cur;
744
745         netifd_fd_add(&proto_fd);
746         glob("./*.sh", 0, NULL, &g);
747         for (i = 0; i < g.gl_pathc; i++)
748                 proto_shell_add_script(g.gl_pathv[i]);
749
750 close_cur:
751         fchdir(main_fd);
752         close(main_fd);
753 }