1 // Copyright 2016 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <dirent.h>
6 #include <errno.h>
7 #include <fcntl.h>
8 #include <inttypes.h>
9 #include <limits.h>
10 #include <stdbool.h>
11 #include <stdint.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <sys/stat.h>
16 #include <unistd.h>
17 
18 #include <fuchsia/device/manager/c/fidl.h>
19 #include <lib/fdio/util.h>
20 #include <pretty/hexdump.h>
21 #include <zircon/syscalls.h>
22 
zxc_dump(int argc,char ** argv)23 int zxc_dump(int argc, char** argv) {
24     int fd;
25     ssize_t len;
26     off_t off;
27     char buf[4096];
28 
29     if (argc != 2) {
30         fprintf(stderr, "usage: dump <filename>\n");
31         return -1;
32     }
33 
34     fd = open(argv[1], O_RDONLY);
35     if (fd < 0) {
36         fprintf(stderr, "error: cannot open '%s'\n", argv[1]);
37         return -1;
38     }
39     off = 0;
40     for (;;) {
41         len = read(fd, buf, sizeof(buf));
42         if (len <= 0) {
43             if (len)
44                 fprintf(stderr, "error: io\n");
45             break;
46         }
47         hexdump8_ex(buf, len, off);
48         off += len;
49     }
50     close(fd);
51     return len;
52 }
53 
zxc_msleep(int argc,char ** argv)54 int zxc_msleep(int argc, char** argv) {
55     if (argc == 2) {
56         zx_nanosleep(zx_deadline_after(ZX_MSEC(atoi(argv[1]))));
57     }
58     return 0;
59 }
60 
modestr(uint32_t mode)61 static const char* modestr(uint32_t mode) {
62     switch (mode & S_IFMT) {
63     case S_IFREG:
64         return "-";
65     case S_IFCHR:
66         return "c";
67     case S_IFBLK:
68         return "b";
69     case S_IFDIR:
70         return "d";
71     default:
72         return "?";
73     }
74 }
75 
zxc_ls(int argc,char ** argv)76 int zxc_ls(int argc, char** argv) {
77     const char* dirn;
78     struct stat s;
79     char tmp[2048];
80     size_t dirln;
81     struct dirent* de;
82     DIR* dir;
83 
84     if ((argc > 1) && !strcmp(argv[1], "-l")) {
85         argc--;
86         argv++;
87     }
88     if (argc < 2) {
89         dirn = ".";
90     } else {
91         dirn = argv[1];
92     }
93     dirln = strlen(dirn);
94 
95     if (argc > 2) {
96         fprintf(stderr, "usage: ls [ <file_or_directory> ]\n");
97         return -1;
98     }
99     if ((dir = opendir(dirn)) == NULL) {
100         if(stat(dirn, &s) == -1) {
101             fprintf(stderr, "error: cannot stat '%s'\n", dirn);
102             return -1;
103         }
104         printf("%s %8jd %s\n", modestr(s.st_mode), (intmax_t)s.st_size, dirn);
105         return 0;
106     }
107     while((de = readdir(dir)) != NULL) {
108         memset(&s, 0, sizeof(struct stat));
109         if ((strlen(de->d_name) + dirln + 2) <= sizeof(tmp)) {
110             snprintf(tmp, sizeof(tmp), "%s/%s", dirn, de->d_name);
111             stat(tmp, &s);
112         }
113         printf("%s %2ju %8jd %s\n", modestr(s.st_mode), s.st_nlink,
114                (intmax_t)s.st_size, de->d_name);
115     }
116     closedir(dir);
117     return 0;
118 }
119 
zxc_list(int argc,char ** argv)120 int zxc_list(int argc, char** argv) {
121     char line[1024];
122     FILE* fp;
123     int num = 1;
124 
125     if (argc != 2) {
126         printf("usage: list <filename>\n");
127         return -1;
128     }
129 
130     fp = fopen(argv[1], "r");
131     if (fp == NULL) {
132         fprintf(stderr, "error: cannot open '%s'\n", argv[1]);
133         return -1;
134     }
135     while (fgets(line, 1024, fp) != NULL) {
136         printf("%5d | %s", num, line);
137         num++;
138     }
139     fclose(fp);
140     return 0;
141 }
142 
file_exists(const char * filename)143 static bool file_exists(const char *filename)
144 {
145     struct stat statbuf;
146     return stat(filename, &statbuf) == 0;
147 }
148 
verify_file(bool is_mv,const char * filename)149 static bool verify_file(bool is_mv, const char *filename)
150 {
151     struct stat statbuf;
152 
153     if (stat(filename, &statbuf) != 0) {
154         fprintf(stderr, "%s: Unable to stat %s\n", is_mv ? "mv" : "cp",
155                 filename);
156         return false;
157     }
158 
159     if (!is_mv && S_ISDIR(statbuf.st_mode)) {
160         fprintf(stderr, "cp: Recursive copy not supported\n");
161         return false;
162     }
163 
164     return true;
165 }
166 
167 // Copy into the destination location, which is not a directory
cp_here(const char * src_name,const char * dest_name,bool dest_exists,bool force)168 static int cp_here(const char *src_name, const char *dest_name,
169                    bool dest_exists, bool force)
170 {
171     if (! verify_file(false, src_name)) {
172         return -1;
173     }
174 
175     char data[4096];
176     int fdi = -1, fdo = -1;
177     int r, wr;
178     int count = 0;
179     if ((fdi = open(src_name, O_RDONLY)) < 0) {
180         fprintf(stderr, "cp: cannot open '%s'\n", src_name);
181         return fdi;
182     }
183     if ((fdo = open(dest_name, O_WRONLY | O_CREAT)) < 0) {
184         if (! force ||
185             unlink(dest_name) != 0 ||
186             (fdo = open(dest_name, O_WRONLY | O_CREAT)) < 0) {
187             fprintf(stderr, "cp: cannot open '%s'\n", dest_name);
188             close(fdi);
189             return fdo;
190         }
191     }
192     for (;;) {
193         if ((r = read(fdi, data, sizeof(data))) < 0) {
194             fprintf(stderr, "cp: failed reading from '%s'\n", src_name);
195             break;
196         }
197         if (r == 0) {
198             break;
199         }
200         if ((wr = write(fdo, data, r)) != r) {
201             fprintf(stderr, "cp: failed writing to '%s'\n", dest_name);
202             r = wr;
203             break;
204         }
205         count += r;
206     }
207     close(fdi);
208     close(fdo);
209     return r;
210 }
211 
212 // Move into the destination location, which is not a directory
mv_here(const char * src_name,const char * dest_name,bool dest_exists,bool force)213 static int mv_here(const char *src_name, const char *dest_name,
214                    bool dest_exists, bool force)
215 {
216     if (! verify_file(true, src_name)) {
217         return -1;
218     }
219 
220     if (rename(src_name, dest_name)) {
221         if (! force ||
222             unlink(dest_name) != 0 ||
223             rename(src_name, dest_name)) {
224             fprintf(stderr, "mv: failed to create '%s'\n", dest_name);
225             return -1;
226         }
227   }
228   return 0;
229 }
230 
231 // Copy a source file into the destination location, which is a directory
mv_or_cp_to_dir(bool is_mv,const char * src_name,const char * dest_name,bool force)232 static int mv_or_cp_to_dir(bool is_mv, const char *src_name,
233                            const char *dest_name, bool force)
234 {
235     if (! verify_file(is_mv, src_name)) {
236         return -1;
237     }
238 
239     const char *filename_start = strrchr(src_name, '/');
240     if (filename_start == NULL) {
241         filename_start = src_name;
242     } else {
243         filename_start++;
244         if (*filename_start == '\0') {
245             fprintf(stderr, "%s: Invalid filename \"%s\"\n",
246                     is_mv ? "mv" : "cp", src_name);
247             return -1;
248         }
249     }
250 
251     size_t path_len = strlen(dest_name);
252     if (path_len == 0) {
253         fprintf(stderr, "%s: Invalid filename \"%s\"\n", is_mv ? "mv" : "cp",
254                 dest_name);
255         return -1;
256     }
257     char full_filename[PATH_MAX];
258     if (dest_name[path_len - 1] == '/') {
259         snprintf(full_filename, PATH_MAX, "%s%s", dest_name, filename_start);
260     } else {
261         snprintf(full_filename, PATH_MAX, "%s/%s", dest_name, filename_start);
262     }
263     if (is_mv) {
264         return mv_here(src_name, full_filename, file_exists(full_filename),
265                        force);
266     } else {
267         return cp_here(src_name, full_filename, file_exists(full_filename),
268                        force);
269     }
270 }
271 
zxc_mv_or_cp(int argc,char ** argv)272 int zxc_mv_or_cp(int argc, char** argv) {
273     bool is_mv = !strcmp(argv[0], "mv");
274     int next_arg = 1;
275     bool force = false;
276     while ((next_arg < argc) && argv[next_arg][0] == '-') {
277         char *next_opt_char = &argv[next_arg][1];
278         if (*next_opt_char == '\0') {
279             goto usage;
280         }
281         do {
282             switch (*next_opt_char) {
283             case 'f':
284                 force = true;
285                 break;
286             default:
287                 goto usage;
288             }
289             next_opt_char++;
290         } while (*next_opt_char);
291         next_arg++;
292     }
293 
294     // Make sure we have at least 2 non-option arguments
295     int src_count = (argc - 1) - next_arg;
296     if (src_count <= 0) {
297         goto usage;
298     }
299 
300     const char *dest_name = argv[argc - 1];
301     bool dest_exists = false;
302     bool dest_isdir = false;
303     struct stat statbuf;
304 
305     if (stat(dest_name, &statbuf) == 0) {
306         dest_exists = true;
307         if (S_ISDIR(statbuf.st_mode)) {
308             dest_isdir = true;
309         }
310     }
311 
312     if (dest_isdir) {
313         do {
314             int result = mv_or_cp_to_dir(is_mv, argv[next_arg], dest_name,
315                                          force);
316             if (result != 0) {
317                 return result;
318             }
319             next_arg++;
320         } while (next_arg < argc - 1);
321         return 0;
322     } else if (src_count > 1) {
323         fprintf(stderr, "%s: destination is not a directory\n", argv[0]);
324         return -1;
325     } else if (is_mv) {
326         return mv_here(argv[next_arg], dest_name, dest_exists, force);
327     } else {
328         return cp_here(argv[next_arg], dest_name, dest_exists, force);
329     }
330 
331 usage:
332     fprintf(stderr, "usage: %s [-f] <src>... <dst>\n", argv[0]);
333     return -1;
334 }
335 
zxc_mkdir(int argc,char ** argv)336 int zxc_mkdir(int argc, char** argv) {
337     // skip "mkdir"
338     argc--;
339     argv++;
340     bool parents = false;
341     if (argc < 1) {
342         fprintf(stderr, "usage: mkdir <path>\n");
343         return -1;
344     }
345     if (!strcmp(argv[0], "-p")) {
346         parents = true;
347         argc--;
348         argv++;
349     }
350     while (argc > 0) {
351         char* dir = argv[0];
352         if (parents) {
353             for (size_t slash = 0u; dir[slash]; slash++) {
354                 if (slash != 0u && dir[slash] == '/') {
355                     dir[slash] = '\0';
356                     if (mkdir(dir, 0755) && errno != EEXIST) {
357                         fprintf(stderr, "error: failed to make directory '%s'\n", dir);
358                         return 0;
359                     }
360                     dir[slash] = '/';
361                 }
362             }
363         }
364         if (mkdir(dir, 0755) && !(parents && errno == EEXIST)) {
365             fprintf(stderr, "error: failed to make directory '%s'\n", dir);
366         }
367         argc--;
368         argv++;
369     }
370     return 0;
371 }
372 
zxc_rm_recursive(int atfd,char * path,bool force)373 static int zxc_rm_recursive(int atfd, char* path, bool force) {
374     struct stat st;
375     if (fstatat(atfd, path, &st, 0)) {
376         return force ? 0 : -1;
377     }
378     if (S_ISDIR(st.st_mode)) {
379         int dfd = openat(atfd, path, 0, O_RDONLY | O_DIRECTORY);
380         if (dfd < 0) {
381             return -1;
382         }
383         DIR* dir = fdopendir(dfd);
384         if (!dir) {
385             close(dfd);
386             return -1;
387         }
388         struct dirent* de;
389         while ((de = readdir(dir)) != NULL) {
390             if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) {
391                 continue;
392             }
393             if (zxc_rm_recursive(dfd, de->d_name, force) < 0) {
394                 closedir(dir);
395                 return -1;
396             }
397         }
398         closedir(dir);
399     }
400     if (unlinkat(atfd, path, 0)) {
401         return -1;
402     } else {
403         return 0;
404     }
405 }
406 
zxc_rm(int argc,char ** argv)407 int zxc_rm(int argc, char** argv) {
408     // skip "rm"
409     argc--;
410     argv++;
411     bool recursive = false;
412     bool force = false;
413     while (argc >= 1 && argv[0][0] == '-') {
414         char *args = &argv[0][1];
415         if (*args == '\0') {
416             goto usage;
417         }
418         do {
419             switch (*args) {
420             case 'r':
421             case 'R':
422                 recursive = true;
423                 break;
424             case 'f':
425                 force = true;
426                 break;
427             default:
428                 goto usage;
429             }
430             args++;
431         } while (*args != '\0');
432         argc--;
433         argv++;
434     }
435     if (argc < 1) {
436         goto usage;
437     }
438 
439     while (argc-- > 0) {
440         if (recursive) {
441             if (zxc_rm_recursive(AT_FDCWD, argv[0], force)) {
442                 goto err;
443             }
444         } else {
445             if (unlink(argv[0])) {
446                 if (errno != ENOENT || !force) {
447                     goto err;
448                 }
449             }
450         }
451         argv++;
452     }
453 
454     return 0;
455 err:
456     fprintf(stderr, "error: failed to delete '%s'\n", argv[0]);
457     return -1;
458 usage:
459     fprintf(stderr, "usage: rm [-frR]... <filename>...\n");
460     return -1;
461 }
462 
send_dmctl(const char * command,size_t length)463 static int send_dmctl(const char* command, size_t length) {
464     int fd = open("/dev/misc/dmctl", O_WRONLY);
465     if (fd < 0) {
466         fprintf(stderr, "error: cannot open dmctl: %d\n", fd);
467         return fd;
468     }
469     zx_handle_t dmctl;
470     zx_status_t status = fdio_get_service_handle(fd, &dmctl);
471     if (status != ZX_OK) {
472         return -1;
473     }
474 
475     if (length > fuchsia_device_manager_COMMAND_MAX) {
476         fprintf(stderr, "error: dmctl command longer than %u bytes: '%.*s'\n",
477                 fuchsia_device_manager_COMMAND_MAX, (int)length, command);
478         return -1;
479     }
480 
481     zx_handle_t local, remote;
482     if (zx_socket_create(0, &remote, &local) != ZX_OK) {
483         zx_handle_close(dmctl);
484         return -1;
485     }
486 
487     zx_status_t call_status;
488     status = fuchsia_device_manager_ExternalControllerExecuteCommand(dmctl, remote, command,
489                                                                      length, &call_status);
490     remote = ZX_HANDLE_INVALID;
491     zx_handle_close(dmctl);
492     if (status != ZX_OK || call_status != ZX_OK) {
493         zx_handle_close(local);
494         return -1;
495     }
496 
497     for (;;) {
498         char buf[32768];
499         size_t actual;
500         if ((status = zx_socket_read(local, 0, buf, sizeof(buf), &actual)) < 0) {
501             if (status == ZX_ERR_SHOULD_WAIT) {
502                 zx_object_wait_one(local, ZX_SOCKET_READABLE | ZX_SOCKET_PEER_CLOSED,
503                                    ZX_TIME_INFINITE, NULL);
504                 continue;
505             }
506             break;
507         }
508         size_t written = 0;
509         while (written < actual) {
510             ssize_t count = write(1, buf + written, actual - written);
511             if (count < 0) {
512                 break;
513             } else {
514                 written += count;
515             }
516         }
517     }
518     zx_handle_close(local);
519 
520     return 0;
521 }
522 
zxc_dm(int argc,char ** argv)523 int zxc_dm(int argc, char** argv) {
524     if (argc != 2) {
525         printf("usage: dm <command>\n");
526         return -1;
527     }
528     return send_dmctl(argv[1], strlen(argv[1]));
529 }
530 
join(char * buffer,size_t buffer_length,int argc,char ** argv)531 static char* join(char* buffer, size_t buffer_length, int argc, char** argv) {
532     size_t total_length = 0u;
533     for (int i = 0; i < argc; ++i) {
534         if (i > 0) {
535             if (total_length + 1 > buffer_length)
536                 return NULL;
537             buffer[total_length++] = ' ';
538         }
539         const char* arg = argv[i];
540         size_t arg_length = strlen(arg);
541         if (total_length + arg_length + 1 > buffer_length)
542             return NULL;
543         strncpy(buffer + total_length, arg, buffer_length - total_length - 1);
544         total_length += arg_length;
545     }
546     return buffer + total_length;
547 }
548 
zxc_k(int argc,char ** argv)549 int zxc_k(int argc, char** argv) {
550     if (argc <= 1) {
551         printf("usage: k <command>\n");
552         return -1;
553     }
554 
555     const char* prefix = "kerneldebug ";
556     char buffer[256];
557 
558     size_t command_length = 0u;
559     // If we detect someone trying to use the LK poweroff/reboot,
560     // divert it to devmgr backed one instead.
561     if (!strcmp(argv[1], "poweroff") || !strcmp(argv[1], "reboot")
562         || !strcmp(argv[1], "reboot-bootloader")) {
563         strcpy(buffer, argv[1]);
564         command_length = strlen(buffer);
565     } else {
566         strcpy(buffer, prefix);
567         size_t prefix_length = strlen(prefix);
568         char* command_end = join(buffer + prefix_length, sizeof(buffer) - prefix_length, argc - 1, &argv[1]);
569         if (!command_end) {
570             fprintf(stderr, "error: kernel debug command too long\n");
571             return -1;
572         }
573         command_length = command_end - buffer;
574     }
575 
576     return send_dmctl(buffer, command_length);
577 }
578