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