]> git.ipfire.org Git - thirdparty/dracut.git/blame - install/dracut-install.c
kernel-modules: add more block driver
[thirdparty/dracut.git] / install / dracut-install.c
CommitLineData
026b81e9
HH
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/* dracut-install.c -- install files and executables
4
5 Copyright (C) 2012 Harald Hoyer
6 Copyright (C) 2012 Red Hat, Inc. All rights reserved.
7
8 This program is free software: you can redistribute it and/or modify
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with this program; If not, see <http://www.gnu.org/licenses/>.
20*/
21
22#define PROGRAM_VERSION_STRING "1"
23
24#ifndef _GNU_SOURCE
25#define _GNU_SOURCE
26#endif
27
28#include <ctype.h>
29#include <errno.h>
30#include <fcntl.h>
31#include <getopt.h>
32#include <libgen.h>
33#include <limits.h>
34#include <stdbool.h>
35#include <stdio.h>
36#include <stdlib.h>
37#include <string.h>
38#include <sys/stat.h>
39#include <sys/types.h>
40#include <sys/wait.h>
41#include <unistd.h>
85854b24 42#include <sys/ioctl.h>
026b81e9
HH
43
44#include "log.h"
45#include "hashmap.h"
46#include "util.h"
47
48static bool arg_hmac = false;
49static bool arg_createdir = false;
50static int arg_loglevel = -1;
51static bool arg_optional = false;
52static bool arg_all = false;
53static bool arg_resolvelazy = false;
54static bool arg_resolvedeps = false;
55static char *destrootdir = NULL;
56
57static Hashmap *items = NULL;
58static Hashmap *items_failed = NULL;
59
e74944ee 60static int dracut_install(const char *src, const char *dst, bool isdir, bool resolvedeps, bool hashdst);
026b81e9
HH
61
62static size_t dir_len(char const *file)
63{
64 size_t length;
3ed08d1e
HH
65
66 if(!file)
67 return 0;
68
026b81e9 69 /* Strip the basename and any redundant slashes before it. */
68c49db9 70 for (length = strlen(file)-1; 0 < length; length--)
f9c7788b 71 if (file[length] == '/' && file[length-1] != '/')
026b81e9
HH
72 break;
73 return length;
74}
75
76static char *convert_abs_rel(const char *from, const char *target)
77{
78 /* we use the 4*MAXPATHLEN, which should not overrun */
79 char relative_from[MAXPATHLEN * 4];
3ed08d1e
HH
80 _cleanup_free_ char *realtarget = NULL;
81 _cleanup_free_ char *target_dir_p = NULL, *realpath_p = NULL;
026b81e9 82 const char *realfrom = from;
599182b1
KR
83 size_t level = 0, fromlevel = 0, targetlevel = 0;
84 int l;
85 size_t i, rl, dirlen;
d9eff33c 86 int ret;
026b81e9 87
3ed08d1e
HH
88 target_dir_p = strdup(target);
89 if (!target_dir_p)
90 return strdup(from);
91
92 dirlen = dir_len(target_dir_p);
93 target_dir_p[dirlen] = '\0';
94 realpath_p = realpath(target_dir_p, NULL);
026b81e9 95
3ed08d1e 96 if (realpath_p == NULL) {
026b81e9
HH
97 log_warning("convert_abs_rel(): target '%s' directory has no realpath.", target);
98 return strdup(from);
99 }
100
f9c7788b
CG
101 /* dir_len() skips double /'s e.g. //lib64, so we can't skip just one
102 * character - need to skip all leading /'s */
103 rl = strlen(target);
104 for (i = dirlen+1; i < rl; ++i)
3ed08d1e 105 if (target_dir_p[i] != '/')
f9c7788b 106 break;
d9eff33c
KR
107 ret = asprintf(&realtarget, "%s/%s", realpath_p, &target_dir_p[i]);
108 if (ret < 0) {
109 log_error("Out of memory!");
110 exit(EXIT_FAILURE);
111 }
026b81e9
HH
112
113 /* now calculate the relative path from <from> to <target> and
114 store it in <relative_from>
115 */
116 relative_from[0] = 0;
117 rl = 0;
118
119 /* count the pathname elements of realtarget */
120 for (targetlevel = 0, i = 0; realtarget[i]; i++)
121 if (realtarget[i] == '/')
122 targetlevel++;
123
124 /* count the pathname elements of realfrom */
125 for (fromlevel = 0, i = 0; realfrom[i]; i++)
126 if (realfrom[i] == '/')
127 fromlevel++;
128
129 /* count the pathname elements, which are common for both paths */
130 for (level = 0, i = 0; realtarget[i] && (realtarget[i] == realfrom[i]); i++)
131 if (realtarget[i] == '/')
132 level++;
133
026b81e9
HH
134 /* add "../" to the relative_from path, until the common pathname is
135 reached */
136 for (i = level; i < targetlevel; i++) {
137 if (i != level)
138 relative_from[rl++] = '/';
139 relative_from[rl++] = '.';
140 relative_from[rl++] = '.';
141 }
142
143 /* set l to the next uncommon pathname element in realfrom */
144 for (l = 1, i = 1; i < level; i++)
145 for (l++; realfrom[l] && realfrom[l] != '/'; l++) ;
146 /* skip next '/' */
147 l++;
148
149 /* append the uncommon rest of realfrom to the relative_from path */
150 for (i = level; i <= fromlevel; i++) {
151 if (rl)
152 relative_from[rl++] = '/';
153 while (realfrom[l] && realfrom[l] != '/')
154 relative_from[rl++] = realfrom[l++];
155 l++;
156 }
157
158 relative_from[rl] = 0;
159 return strdup(relative_from);
160}
161
162static int ln_r(const char *src, const char *dst)
163{
164 int ret;
3ed08d1e
HH
165 _cleanup_free_ const char *points_to = convert_abs_rel(src, dst);
166
026b81e9
HH
167 log_info("ln -s '%s' '%s'", points_to, dst);
168 ret = symlink(points_to, dst);
169
170 if (ret != 0) {
171 log_error("ERROR: ln -s '%s' '%s': %m", points_to, dst);
026b81e9
HH
172 return 1;
173 }
174
026b81e9
HH
175 return 0;
176}
177
85854b24
HH
178/* Perform the O(1) btrfs clone operation, if possible.
179 Upon success, return 0. Otherwise, return -1 and set errno. */
180static inline int clone_file(int dest_fd, int src_fd)
181{
182#undef BTRFS_IOCTL_MAGIC
183#define BTRFS_IOCTL_MAGIC 0x94
184#undef BTRFS_IOC_CLONE
185#define BTRFS_IOC_CLONE _IOW (BTRFS_IOCTL_MAGIC, 9, int)
186 return ioctl(dest_fd, BTRFS_IOC_CLONE, src_fd);
187}
188
189static bool use_clone = true;
190
026b81e9
HH
191static int cp(const char *src, const char *dst)
192{
193 int pid;
3ed08d1e 194 int ret = 0;
85854b24 195
f6c2faeb 196 if (use_clone) {
85854b24 197 struct stat sb;
3ed08d1e 198 _cleanup_close_ int dest_desc = -1, source_desc = -1;
85854b24
HH
199
200 if (lstat(src, &sb) != 0)
201 goto normal_copy;
202
203 if (S_ISLNK(sb.st_mode))
204 goto normal_copy;
205
206 source_desc = open(src, O_RDONLY | O_CLOEXEC);
207 if (source_desc < 0)
208 goto normal_copy;
026b81e9 209
85854b24 210 dest_desc =
f6c2faeb
HH
211 open(dst, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC,
212 (sb.st_mode) & (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO));
85854b24
HH
213
214 if (dest_desc < 0) {
85854b24
HH
215 goto normal_copy;
216 }
217
218 ret = clone_file(dest_desc, source_desc);
3ed08d1e 219
85854b24 220 if (ret == 0) {
6f4c2dad 221 struct timeval tv[2];
85854b24 222 if (fchown(dest_desc, sb.st_uid, sb.st_gid) != 0)
599182b1 223 fchown(dest_desc, (__uid_t)-1, sb.st_gid);
6f4c2dad
HH
224 tv[0].tv_sec = sb.st_atime;
225 tv[0].tv_usec = 0;
226 tv[1].tv_sec = sb.st_mtime;
227 tv[1].tv_usec = 0;
228 futimes(dest_desc, tv);
85854b24
HH
229 return ret;
230 }
231 close(dest_desc);
3ed08d1e 232 dest_desc = -1;
85854b24
HH
233 /* clone did not work, remove the file */
234 unlink(dst);
235 /* do not try clone again */
236 use_clone = false;
237 }
238
239 normal_copy:
026b81e9
HH
240 pid = fork();
241 if (pid == 0) {
6f4c2dad 242 execlp("cp", "cp", "--reflink=auto", "--sparse=auto", "--preserve=mode,timestamps", "-fL", src, dst, NULL);
026b81e9
HH
243 _exit(EXIT_FAILURE);
244 }
245
85854b24 246 while (waitpid(pid, &ret, 0) < 0) {
026b81e9 247 if (errno != EINTR) {
85854b24 248 ret = -1;
3ed08d1e 249 log_error("Failed: cp --reflink=auto --sparse=auto --preserve=mode,timestamps -fL %s %s", src, dst);
026b81e9
HH
250 break;
251 }
252 }
3ed08d1e 253 log_debug("cp ret = %d", ret);
85854b24 254 return ret;
026b81e9
HH
255}
256
791532b0
HH
257static int library_install(const char *src, const char *lib)
258{
259 _cleanup_free_ char *p = NULL;
260 _cleanup_free_ char *pdir = NULL, *ppdir = NULL, *clib = NULL;
261 char *q;
262 int r, ret = 0;
263
264 p = strdup(lib);
265
266 r = dracut_install(p, p, false, false, true);
267 if (r != 0)
268 log_error("ERROR: failed to install '%s' for '%s'", p, src);
269 else
270 log_debug("Lib install: '%s'", p);
271 ret += r;
272
273 /* also install lib.so for lib.so.* files */
274 q = strstr(p, ".so.");
275 if (q) {
276 q[3] = '\0';
277
278 /* ignore errors for base lib symlink */
279 if (dracut_install(p, p, false, false, true) == 0)
280 log_debug("Lib install: '%s'", p);
281 }
282
283 /* Also try to install the same library from one directory above.
284 This fixes the case, where only the HWCAP lib would be installed
285 # ldconfig -p|fgrep libc.so
286 libc.so.6 (libc6,64bit, hwcap: 0x0000001000000000, OS ABI: Linux 2.6.32) => /lib64/power6/libc.so.6
287 libc.so.6 (libc6,64bit, hwcap: 0x0000000000000200, OS ABI: Linux 2.6.32) => /lib64/power6x/libc.so.6
288 libc.so.6 (libc6,64bit, OS ABI: Linux 2.6.32) => /lib64/libc.so.6
289 */
290
291 free(p);
292 p = strdup(lib);
293
294 pdir = dirname(p);
295 if (!pdir)
296 return ret;
297
298 pdir = strdup(pdir);
299 ppdir = dirname(pdir);
300 if (!ppdir)
301 return ret;
302
303 ppdir = strdup(ppdir);
304
305 strcpy(p, lib);
306
307 clib = strjoin(ppdir, "/", basename(p), NULL);
308 if (dracut_install(clib, clib, false, false, true) == 0)
309 log_debug("Lib install: '%s'", clib);
310 /* also install lib.so for lib.so.* files */
311 q = strstr(clib, ".so.");
312 if (q) {
313 q[3] = '\0';
314
315 /* ignore errors for base lib symlink */
316 if (dracut_install(clib, clib, false, false, true) == 0)
317 log_debug("Lib install: '%s'", p);
318 }
319
320 return ret;
321}
322
026b81e9
HH
323static int resolve_deps(const char *src)
324{
325 int ret = 0;
326
e0904b27 327 _cleanup_free_ char *buf = malloc(LINE_MAX);
026b81e9 328 size_t linesize = LINE_MAX;
3ed08d1e
HH
329 _cleanup_pclose_ FILE *fptr = NULL;
330 _cleanup_free_ char *cmd = NULL;
026b81e9
HH
331
332 if (strstr(src, ".so") == 0) {
3ed08d1e 333 _cleanup_close_ int fd = -1;
026b81e9 334 fd = open(src, O_RDONLY | O_CLOEXEC);
3ed08d1e
HH
335 if (fd < 0)
336 return -errno;
337
026b81e9
HH
338 read(fd, buf, LINE_MAX);
339 buf[LINE_MAX - 1] = '\0';
026b81e9
HH
340 if (buf[0] == '#' && buf[1] == '!') {
341 /* we have a shebang */
342 char *p, *q;
343 for (p = &buf[2]; *p && isspace(*p); p++) ;
344 for (q = p; *q && (!isspace(*q)); q++) ;
345 *q = '\0';
346 log_debug("Script install: '%s'", p);
e74944ee 347 ret = dracut_install(p, p, false, true, false);
026b81e9
HH
348 if (ret != 0)
349 log_error("ERROR: failed to install '%s'", p);
350 return ret;
351 }
352 }
353
354 /* run ldd */
3ed08d1e 355 ret = asprintf(&cmd, "ldd %s 2>&1", src);
d9eff33c
KR
356 if (ret < 0) {
357 log_error("Out of memory!");
358 exit(EXIT_FAILURE);
359 }
360
3ed08d1e
HH
361 ret = 0;
362
026b81e9
HH
363 fptr = popen(cmd, "r");
364
365 while (!feof(fptr)) {
366 char *p, *q;
367
368 if (getline(&buf, &linesize, fptr) <= 0)
369 continue;
370
371 log_debug("ldd: '%s'", buf);
372
b4dc22ca 373 if (strstr(buf, "you do not have execution permission")) {
599182b1 374 log_error("%s", buf);
b4dc22ca
HH
375 ret+=1;
376 break;
377 }
378
026b81e9
HH
379 if (strstr(buf, "not a dynamic executable"))
380 break;
381
a9231107
HH
382 if (strstr(buf, "loader cannot load itself"))
383 break;
384
43a050e5
HH
385 if (strstr(buf, "not regular file"))
386 break;
87dc81a1
HH
387
388 if (strstr(buf, "cannot read header"))
389 break;
390
391 if (strstr(buf, destrootdir))
392 break;
43a050e5 393
791532b0 394 p = strchr(buf, '/');
026b81e9 395 if (p) {
026b81e9
HH
396 for (q = p; *q && *q != ' ' && *q != '\n'; q++) ;
397 *q = '\0';
791532b0
HH
398
399 ret += library_install(src, p);
400
026b81e9
HH
401 }
402 }
026b81e9
HH
403
404 return ret;
405}
406
407/* Install ".<filename>.hmac" file for FIPS self-checks */
cce471c9 408static int hmac_install(const char *src, const char *dst, const char *hmacpath)
026b81e9 409{
3ed08d1e
HH
410 _cleanup_free_ char *srcpath = strdup(src);
411 _cleanup_free_ char *dstpath = strdup(dst);
412 _cleanup_free_ char *srchmacname = NULL;
413 _cleanup_free_ char *dsthmacname = NULL;
d9eff33c 414 int ret;
3ed08d1e
HH
415
416 if (!(srcpath && dstpath))
417 return -ENOMEM;
418
026b81e9
HH
419 size_t dlen = dir_len(src);
420
421 if (endswith(src, ".hmac"))
422 return 0;
423
cce471c9
MB
424 if (!hmacpath) {
425 hmac_install(src, dst, "/lib/fipscheck");
426 hmac_install(src, dst, "/lib64/fipscheck");
6f4c2dad
HH
427 hmac_install(src, dst, "/lib/hmaccalc");
428 hmac_install(src, dst, "/lib64/hmaccalc");
cce471c9
MB
429 }
430
026b81e9
HH
431 srcpath[dlen] = '\0';
432 dstpath[dir_len(dst)] = '\0';
cce471c9 433 if (hmacpath) {
d9eff33c
KR
434 ret = asprintf(&srchmacname, "%s/%s.hmac", hmacpath, &src[dlen + 1]);
435 if (ret < 0) {
436 log_error("Out of memory!");
437 exit(EXIT_FAILURE);
438 }
439
440 ret = asprintf(&dsthmacname, "%s/%s.hmac", hmacpath, &src[dlen + 1]);
441 if (ret < 0) {
442 log_error("Out of memory!");
443 exit(EXIT_FAILURE);
444 }
cce471c9 445 } else {
d9eff33c
KR
446 ret = asprintf(&srchmacname, "%s/.%s.hmac", srcpath, &src[dlen + 1]);
447 if (ret < 0) {
448 log_error("Out of memory!");
449 exit(EXIT_FAILURE);
450 }
451
452 ret = asprintf(&dsthmacname, "%s/.%s.hmac", dstpath, &src[dlen + 1]);
453 if (ret < 0) {
454 log_error("Out of memory!");
455 exit(EXIT_FAILURE);
456 }
cce471c9 457 }
026b81e9 458 log_debug("hmac cp '%s' '%s')", srchmacname, dsthmacname);
e74944ee 459 dracut_install(srchmacname, dsthmacname, false, false, true);
026b81e9
HH
460 return 0;
461}
462
e74944ee 463static int dracut_install(const char *src, const char *dst, bool isdir, bool resolvedeps, bool hashdst)
026b81e9
HH
464{
465 struct stat sb, db;
3ed08d1e
HH
466 _cleanup_free_ char *fulldstpath = NULL;
467 _cleanup_free_ char *fulldstdir = NULL;
026b81e9
HH
468 int ret;
469 bool src_exists = true;
3ed08d1e
HH
470 char *i = NULL;
471 char *existing;
026b81e9
HH
472
473 log_debug("dracut_install('%s', '%s')", src, dst);
474
475 existing = hashmap_get(items_failed, src);
476 if (existing) {
477 if (strcmp(existing, src) == 0) {
478 log_debug("hash hit items_failed for '%s'", src);
479 return 1;
480 }
481 }
482
e74944ee
HH
483 if (hashdst) {
484 existing = hashmap_get(items, dst);
485 if (existing) {
486 if (strcmp(existing, dst) == 0) {
487 log_debug("hash hit items for '%s'", dst);
488 return 0;
489 }
026b81e9
HH
490 }
491 }
492
493 if (lstat(src, &sb) < 0) {
494 src_exists = false;
495 if (!isdir) {
496 i = strdup(src);
497 hashmap_put(items_failed, i, i);
498 /* src does not exist */
499 return 1;
500 }
501 }
502
503 i = strdup(dst);
3ed08d1e
HH
504 if (!i)
505 return -ENOMEM;
506
026b81e9
HH
507 hashmap_put(items, i, i);
508
d9eff33c
KR
509 ret = asprintf(&fulldstpath, "%s%s", destrootdir, dst);
510 if (ret < 0) {
511 log_error("Out of memory!");
512 exit(EXIT_FAILURE);
513 }
026b81e9
HH
514
515 ret = stat(fulldstpath, &sb);
516
517 if (ret != 0 && (errno != ENOENT)) {
518 log_error("ERROR: stat '%s': %m", fulldstpath);
519 return 1;
520 }
521
522 if (ret == 0) {
43a050e5 523 if (resolvedeps && S_ISREG(sb.st_mode) && (sb.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) {
68318328
CG
524 log_debug("'%s' already exists, but checking for any deps", fulldstpath);
525 ret = resolve_deps(src);
526 } else
527 log_debug("'%s' already exists", fulldstpath);
528
026b81e9 529 /* dst does already exist */
68318328 530 return ret;
026b81e9
HH
531 }
532
533 /* check destination directory */
534 fulldstdir = strdup(fulldstpath);
535 fulldstdir[dir_len(fulldstdir)] = '\0';
536
537 ret = stat(fulldstdir, &db);
538
539 if (ret < 0) {
3ed08d1e
HH
540 _cleanup_free_ char *dname = NULL;
541
026b81e9
HH
542 if (errno != ENOENT) {
543 log_error("ERROR: stat '%s': %m", fulldstdir);
544 return 1;
545 }
546 /* create destination directory */
547 log_debug("dest dir '%s' does not exist", fulldstdir);
548 dname = strdup(dst);
3ed08d1e
HH
549 if (!dname)
550 return 1;
551
026b81e9 552 dname[dir_len(dname)] = '\0';
e74944ee 553 ret = dracut_install(dname, dname, true, false, true);
026b81e9 554
026b81e9
HH
555 if (ret != 0) {
556 log_error("ERROR: failed to create directory '%s'", fulldstdir);
026b81e9
HH
557 return 1;
558 }
559 }
560
026b81e9
HH
561 if (isdir && !src_exists) {
562 log_info("mkdir '%s'", fulldstpath);
3ed08d1e
HH
563 ret = mkdir(fulldstpath, 0755);
564 return ret;
026b81e9
HH
565 }
566
567 /* ready to install src */
568
569 if (S_ISDIR(sb.st_mode)) {
570 log_info("mkdir '%s'", fulldstpath);
3ed08d1e
HH
571 ret = mkdir(fulldstpath, sb.st_mode | S_IWUSR);
572 return ret;
026b81e9
HH
573 }
574
575 if (S_ISLNK(sb.st_mode)) {
3ed08d1e 576 _cleanup_free_ char *abspath = NULL;
026b81e9
HH
577
578 abspath = realpath(src, NULL);
579
580 if (abspath == NULL)
581 return 1;
582
e74944ee 583 if (dracut_install(abspath, abspath, false, resolvedeps, hashdst)) {
026b81e9
HH
584 log_debug("'%s' install error", abspath);
585 return 1;
586 }
587
588 if (lstat(abspath, &sb) != 0) {
589 log_debug("lstat '%s': %m", abspath);
590 return 1;
591 }
592
593 if (lstat(fulldstpath, &sb) != 0) {
3ed08d1e 594 _cleanup_free_ char *absdestpath = NULL;
026b81e9 595
d9eff33c
KR
596 ret = asprintf(&absdestpath, "%s%s", destrootdir, abspath);
597 if (ret < 0) {
598 log_error("Out of memory!");
599 exit(EXIT_FAILURE);
600 }
026b81e9
HH
601
602 ln_r(absdestpath, fulldstpath);
026b81e9
HH
603 }
604
026b81e9
HH
605 if (arg_hmac) {
606 /* copy .hmac files also */
cce471c9 607 hmac_install(src, dst, NULL);
026b81e9
HH
608 }
609
610 return 0;
611 }
612
613 if (sb.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) {
614 if (resolvedeps)
615 ret += resolve_deps(src);
616 if (arg_hmac) {
617 /* copy .hmac files also */
cce471c9 618 hmac_install(src, dst, NULL);
026b81e9
HH
619 }
620 }
621
3ed08d1e 622 log_debug("dracut_install ret = %d", ret);
026b81e9
HH
623 log_info("cp '%s' '%s'", src, fulldstpath);
624 ret += cp(src, fulldstpath);
3ed08d1e
HH
625
626 log_debug("dracut_install ret = %d", ret);
627
026b81e9
HH
628 return ret;
629}
630
631static void item_free(char *i)
632{
633 assert(i);
634 free(i);
635}
636
637static void usage(int status)
3ed08d1e
HH
638{
639 /* */
640 printf("Usage: %s -D DESTROOTDIR [OPTION]... -a SOURCE...\n"
641 "or: %s -D DESTROOTDIR [OPTION]... SOURCE DEST\n"
642 "\n"
643 "Install SOURCE to DEST in DESTROOTDIR with all needed dependencies.\n"
644 "\n"
645 " -D --destrootdir Install all files to DESTROOTDIR as the root\n"
646 " -a --all Install all SOURCE arguments to DESTROOTDIR\n"
647 " -o --optional If SOURCE does not exist, do not fail\n"
648 " -d --dir SOURCE is a directory\n"
649 " -l --ldd Also install shebang executables and libraries\n"
650 " -R --resolvelazy Only install shebang executables and libraries\n"
651 " for all SOURCE files\n"
652 " -H --fips Also install all '.SOURCE.hmac' files\n"
653 " -v --verbose Show more output\n"
654 " --debug Show debug output\n"
655 " --version Show package version\n"
656 " -h --help Show this help\n"
657 "\n"
658 "Example:\n"
659 "# mkdir -p /var/tmp/test-root\n"
660 "# %s -D /var/tmp/test-root --ldd -a sh tr\n"
661 "# tree /var/tmp/test-root\n"
662 "/var/tmp/test-root\n"
663 "|-- lib64 -> usr/lib64\n"
664 "`-- usr\n"
665 " |-- bin\n"
666 " | |-- bash\n"
667 " | |-- sh -> bash\n"
668 " | `-- tr\n"
669 " `-- lib64\n"
670 " |-- ld-2.15.90.so\n"
671 " |-- ld-linux-x86-64.so.2 -> ld-2.15.90.so\n"
672 " |-- libc-2.15.90.so\n"
673 " |-- libc.so\n"
674 " |-- libc.so.6 -> libc-2.15.90.so\n"
675 " |-- libdl-2.15.90.so\n"
676 " |-- libdl.so -> libdl-2.15.90.so\n"
677 " |-- libdl.so.2 -> libdl-2.15.90.so\n"
678 " |-- libtinfo.so.5 -> libtinfo.so.5.9\n"
679 " `-- libtinfo.so.5.9\n"
680 , program_invocation_short_name, program_invocation_short_name, program_invocation_short_name);
026b81e9
HH
681 exit(status);
682}
683
684static int parse_argv(int argc, char *argv[])
685{
686 int c;
687
688 enum {
689 ARG_VERSION = 0x100,
690 ARG_DEBUG
691 };
692
3ed08d1e 693 static struct option const options[] = {
026b81e9
HH
694 {"help", no_argument, NULL, 'h'},
695 {"version", no_argument, NULL, ARG_VERSION},
696 {"dir", no_argument, NULL, 'd'},
697 {"debug", no_argument, NULL, ARG_DEBUG},
698 {"verbose", no_argument, NULL, 'v'},
699 {"ldd", no_argument, NULL, 'l'},
700 {"resolvelazy", no_argument, NULL, 'R'},
701 {"optional", no_argument, NULL, 'o'},
702 {"all", no_argument, NULL, 'a'},
703 {"fips", no_argument, NULL, 'H'},
704 {"destrootdir", required_argument, NULL, 'D'},
705 {NULL, 0, NULL, 0}
706 };
707
6a736cc1 708 while ((c = getopt_long(argc, argv, "adhloD:HR", options, NULL)) != -1) {
026b81e9
HH
709 switch (c) {
710 case ARG_VERSION:
711 puts(PROGRAM_VERSION_STRING);
712 return 0;
713 case 'd':
714 arg_createdir = true;
715 break;
716 case ARG_DEBUG:
717 arg_loglevel = LOG_DEBUG;
718 break;
719 case 'v':
720 arg_loglevel = LOG_INFO;
721 break;
722 case 'o':
723 arg_optional = true;
724 break;
725 case 'l':
726 arg_resolvedeps = true;
727 break;
728 case 'R':
729 arg_resolvelazy = true;
730 break;
731 case 'a':
732 arg_all = true;
733 break;
734 case 'D':
735 destrootdir = strdup(optarg);
736 break;
737 case 'H':
738 arg_hmac = true;
739 break;
740 case 'h':
741 usage(EXIT_SUCCESS);
742 break;
743 default:
744 usage(EXIT_FAILURE);
745 }
746 }
747
748 if (!optind || optind == argc) {
34e43ceb 749 log_error("No SOURCE argument given");
026b81e9
HH
750 usage(EXIT_FAILURE);
751 }
752
753 return 1;
754}
755
756static int resolve_lazy(int argc, char **argv)
757{
758 int i;
599182b1 759 size_t destrootdirlen = strlen(destrootdir);
026b81e9
HH
760 int ret = 0;
761 char *item;
762 for (i = 0; i < argc; i++) {
763 const char *src = argv[i];
764 char *p = argv[i];
765 char *existing;
766
767 log_debug("resolve_deps('%s')", src);
768
769 if (strstr(src, destrootdir)) {
770 p = &argv[i][destrootdirlen];
771 }
772
773 existing = hashmap_get(items, p);
774 if (existing) {
775 if (strcmp(existing, p) == 0)
776 continue;
777 }
778
779 item = strdup(p);
780 hashmap_put(items, item, item);
781
782 ret += resolve_deps(src);
783 }
784 return ret;
785}
786
868eba13
HH
787static char *find_binary(const char *src)
788{
3ed08d1e 789 _cleanup_free_ char *path = NULL;
868eba13
HH
790 char *p, *q;
791 bool end = false;
792 char *newsrc = NULL;
d9eff33c
KR
793 int ret;
794
868eba13
HH
795 path = getenv("PATH");
796
797 if (path == NULL) {
798 log_error("PATH is not set");
799 exit(EXIT_FAILURE);
800 }
801 path = strdup(path);
802 p = path;
3ed08d1e
HH
803
804 if (path == NULL) {
805 log_error("Out of memory!");
806 exit(EXIT_FAILURE);
807 }
808
868eba13
HH
809 log_debug("PATH=%s", path);
810
811 do {
812 struct stat sb;
813
814 for (q = p; *q && *q != ':'; q++) ;
815
816 if (*q == '\0')
817 end = true;
818 else
819 *q = '\0';
820
d9eff33c
KR
821 ret = asprintf(&newsrc, "%s/%s", p, src);
822 if (ret < 0) {
3ed08d1e
HH
823 log_error("Out of memory!");
824 exit(EXIT_FAILURE);
825 }
826
868eba13
HH
827 p = q + 1;
828
829 if (stat(newsrc, &sb) != 0) {
830 log_debug("stat(%s) != 0", newsrc);
831 free(newsrc);
832 newsrc = NULL;
833 continue;
834 }
835
836 end = true;
837
838 } while (!end);
839
868eba13
HH
840 if (newsrc)
841 log_debug("find_binary(%s) == %s", src, newsrc);
3ed08d1e 842
868eba13
HH
843 return newsrc;
844}
845
846static int install_one(const char *src, const char *dst)
847{
8974102f 848 int r = EXIT_SUCCESS;
868eba13
HH
849 int ret;
850
851 if (strchr(src, '/') == NULL) {
852 char *newsrc = find_binary(src);
853 if (newsrc) {
854 log_debug("dracut_install '%s' '%s'", newsrc, dst);
855 ret = dracut_install(newsrc, dst, arg_createdir, arg_resolvedeps, true);
856 if (ret == 0) {
857 log_debug("dracut_install '%s' '%s' OK", newsrc, dst);
858 }
859 free(newsrc);
860 } else {
861 ret = -1;
862 }
863 } else {
864 ret = dracut_install(src, dst, arg_createdir, arg_resolvedeps, true);
865 }
866
867 if ((ret != 0) && (!arg_optional)) {
868 log_error("ERROR: installing '%s' to '%s'", src, dst);
869 r = EXIT_FAILURE;
870 }
871
872 return r;
873}
874
026b81e9
HH
875static int install_all(int argc, char **argv)
876{
8974102f 877 int r = EXIT_SUCCESS;
026b81e9
HH
878 int i;
879 for (i = 0; i < argc; i++) {
880 int ret;
881 log_debug("Handle '%s'", argv[i]);
882
883 if (strchr(argv[i], '/') == NULL) {
3ed08d1e 884 _cleanup_free_ char *newsrc = find_binary(argv[i]);
868eba13 885 if (newsrc) {
026b81e9 886 log_debug("dracut_install '%s'", newsrc);
868eba13 887 ret = dracut_install(newsrc, newsrc, arg_createdir, arg_resolvedeps, true);
026b81e9 888 if (ret == 0) {
026b81e9
HH
889 log_debug("dracut_install '%s' OK", newsrc);
890 }
868eba13
HH
891 } else {
892 ret = -1;
893 }
894
026b81e9 895 } else {
3ed08d1e 896 _cleanup_free_ char *dest = strdup(argv[i]);
e74944ee 897 ret = dracut_install(argv[i], dest, arg_createdir, arg_resolvedeps, true);
026b81e9
HH
898 }
899
85854b24 900 if ((ret != 0) && (!arg_optional)) {
026b81e9
HH
901 log_error("ERROR: installing '%s'", argv[i]);
902 r = EXIT_FAILURE;
903 }
904 }
905 return r;
906}
907
908int main(int argc, char **argv)
909{
910 int r;
911 char *i;
912
913 r = parse_argv(argc, argv);
914 if (r <= 0)
915 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
916
917 log_set_target(LOG_TARGET_CONSOLE);
918 log_parse_environment();
919
920 if (arg_loglevel >= 0)
921 log_set_max_level(arg_loglevel);
922
923 log_open();
924
925 umask(0022);
926
f6c2faeb 927 if (destrootdir == NULL || strlen(destrootdir) == 0) {
026b81e9 928 destrootdir = getenv("DESTROOTDIR");
f6c2faeb 929 if (destrootdir == NULL || strlen(destrootdir) == 0) {
026b81e9
HH
930 log_error("Environment DESTROOTDIR or argument -D is not set!");
931 usage(EXIT_FAILURE);
932 }
933 destrootdir = strdup(destrootdir);
934 }
935
f6c2faeb
HH
936 if (strcmp(destrootdir, "/") == 0) {
937 log_error("Environment DESTROOTDIR or argument -D is set to '/'!");
938 usage(EXIT_FAILURE);
78021eac 939 }
f6c2faeb 940
78021eac
HH
941 i = destrootdir;
942 destrootdir = realpath(destrootdir, NULL);
943 if (!destrootdir) {
944 log_error("Environment DESTROOTDIR or argument -D is set to '%s': %m", i);
945 r = EXIT_FAILURE;
946 goto finish;
f6c2faeb 947 }
78021eac 948 free(i);
f6c2faeb 949
026b81e9
HH
950 items = hashmap_new(string_hash_func, string_compare_func);
951 items_failed = hashmap_new(string_hash_func, string_compare_func);
952
953 if (!items || !items_failed) {
954 log_error("Out of memory");
955 r = EXIT_FAILURE;
956 goto finish;
957 }
958
959 r = EXIT_SUCCESS;
960
961 if (((optind + 1) < argc) && (strcmp(argv[optind + 1], destrootdir) == 0)) {
962 /* ugly hack for compat mode "inst src $destrootdir" */
963 if ((optind + 2) == argc) {
964 argc--;
965 } else {
966 /* ugly hack for compat mode "inst src $destrootdir dst" */
967 if ((optind + 3) == argc) {
968 argc--;
969 argv[optind + 1] = argv[optind + 2];
970 }
971 }
972 }
973
974 if (arg_resolvelazy) {
975 r = resolve_lazy(argc - optind, &argv[optind]);
976 } else if (arg_all || (argc - optind > 2) || ((argc - optind) == 1)) {
977 r = install_all(argc - optind, &argv[optind]);
978 } else {
979 /* simple "inst src dst" */
868eba13 980 r = install_one(argv[optind], argv[optind + 1]);
026b81e9
HH
981 }
982
983 if (arg_optional)
984 r = EXIT_SUCCESS;
985
986 finish:
987
988 while ((i = hashmap_steal_first(items)))
989 item_free(i);
990
991 while ((i = hashmap_steal_first(items_failed)))
992 item_free(i);
993
994 hashmap_free(items);
995 hashmap_free(items_failed);
996
997 free(destrootdir);
998
999 return r;
1000}