1 /**************************************************************************
3 * Copyright (c) 2004-18 Simon Peter
4 * Portions Copyright (c) 2007 Alexander Larsson
8 * Permission is hereby granted, free of charge, to any person obtaining a copy
9 * of this software and associated documentation files (the "Software"), to deal
10 * in the Software without restriction, including without limitation the rights
11 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 * copies of the Software, and to permit persons to whom the Software is
13 * furnished to do so, subject to the following conditions:
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26 **************************************************************************/
28 #ident "AppImage by Simon Peter, http://appimage.org/"
32 #include "squashfuse.h"
33 #include <squashfs_fs.h>
38 #include <sys/types.h>
51 #include <appimage/appimage_shared.h>
57 #include "squashfuse_dlopen.h"
59 /* Exit status to use when launching an AppImage fails.
60 * For applications that assign meanings to exit status codes (e.g. rsync),
61 * we avoid "cluttering" pre-defined exit status codes by using 127 which
62 * is known to alias an application exit status and also known as launcher
63 * error, see SYSTEM(3POSIX).
65 #define EXIT_EXECERROR 127 /* Execution error exit status. */
68 extern int notify(char *title
, char *body
, int timeout
);
71 static ssize_t fs_offset
; // The offset at which a filesystem image is expected = end of this ELF
73 static void die(const char *msg
) {
74 fprintf(stderr
, "%s\n", msg
);
78 /* Check whether directory is writable */
79 bool is_writable_directory(char* str
) {
80 if(access(str
, W_OK
) == 0) {
87 bool startsWith(const char *pre
, const char *str
)
89 size_t lenpre
= strlen(pre
),
91 return lenstr
< lenpre
? false : strncmp(pre
, str
, lenpre
) == 0;
94 /* Fill in a stat structure. Does not set st_ino */
95 sqfs_err
private_sqfs_stat(sqfs
*fs
, sqfs_inode
*inode
, struct stat
*st
) {
96 sqfs_err err
= SQFS_OK
;
99 memset(st
, 0, sizeof(*st
));
100 st
->st_mode
= inode
->base
.mode
;
101 st
->st_nlink
= inode
->nlink
;
102 st
->st_mtime
= st
->st_ctime
= st
->st_atime
= inode
->base
.mtime
;
104 if (S_ISREG(st
->st_mode
)) {
105 /* FIXME: do symlinks, dirs, etc have a size? */
106 st
->st_size
= inode
->xtra
.reg
.file_size
;
107 st
->st_blocks
= st
->st_size
/ 512;
108 } else if (S_ISBLK(st
->st_mode
) || S_ISCHR(st
->st_mode
)) {
109 st
->st_rdev
= sqfs_makedev(inode
->xtra
.dev
.major
,
110 inode
->xtra
.dev
.minor
);
111 } else if (S_ISLNK(st
->st_mode
)) {
112 st
->st_size
= inode
->xtra
.symlink_size
;
115 st
->st_blksize
= fs
->sb
.block_size
; /* seriously? */
117 err
= sqfs_id_get(fs
, inode
->base
.uid
, &id
);
121 err
= sqfs_id_get(fs
, inode
->base
.guid
, &id
);
129 /* ================= End ELF parsing */
131 extern int fusefs_main(int argc
, char *argv
[], void (*mounted
) (void));
132 // extern void ext2_quit(void);
134 static pid_t fuse_pid
;
135 static int keepalive_pipe
[2];
138 write_pipe_thread (void *arg
)
142 // sprintf(stderr, "Called write_pipe_thread");
143 memset (c
, 'x', sizeof (c
));
145 /* Write until we block, on broken pipe, exit */
146 res
= write (keepalive_pipe
[1], c
, sizeof (c
));
148 kill (fuse_pid
, SIGTERM
);
160 pthread_create(&thread
, NULL
, write_pipe_thread
, keepalive_pipe
);
163 char* getArg(int argc
, char *argv
[],char chr
)
166 for (i
=1; i
<argc
; ++i
)
167 if ((argv
[i
][0]=='-') && (argv
[i
][1]==chr
))
168 return &(argv
[i
][2]);
172 /* mkdir -p implemented in C, needed for https://github.com/AppImage/AppImageKit/issues/333
173 * https://gist.github.com/JonathonReinhart/8c0d90191c38af2dcadb102c4e202950 */
175 mkdir_p(const char* const path
)
177 /* Adapted from http://stackoverflow.com/a/2336245/119527 */
178 const size_t len
= strlen(path
);
179 char _path
[PATH_MAX
];
184 /* Copy string so its mutable */
185 if (len
> sizeof(_path
)-1) {
186 errno
= ENAMETOOLONG
;
191 /* Iterate the string */
192 for (p
= _path
+ 1; *p
; p
++) {
194 /* Temporarily truncate */
197 if (mkdir(_path
, 0755) != 0) {
206 if (mkdir(_path
, 0755) != 0) {
215 print_help(const char *appimage_path
)
217 // TODO: "--appimage-list List content from embedded filesystem image\n"
219 "AppImage options:\n\n"
220 " --appimage-extract [<pattern>] Extract content from embedded filesystem image\n"
221 " If pattern is passed, only extract matching files\n"
222 " --appimage-help Print this help\n"
223 " --appimage-mount Mount embedded filesystem image and print\n"
224 " mount point and wait for kill with Ctrl-C\n"
225 " --appimage-offset Print byte offset to start of embedded\n"
226 " filesystem image\n"
227 " --appimage-portable-home Create a portable home folder to use as $HOME\n"
228 " --appimage-portable-config Create a portable config folder to use as\n"
229 " $XDG_CONFIG_HOME\n"
230 " --appimage-signature Print digital signature embedded in AppImage\n"
231 " --appimage-updateinfo[rmation] Print update info embedded in AppImage\n"
232 " --appimage-version Print version of AppImageKit\n"
236 " If you would like the application contained inside this AppImage to store its\n"
237 " data alongside this AppImage rather than in your home directory, then you can\n"
238 " place a directory named\n"
242 " Or you can invoke this AppImage with the --appimage-portable-home option,\n"
243 " which will create this directory for you. As long as the directory exists\n"
244 " and is neither moved nor renamed, the application contained inside this\n"
245 " AppImage will store its data in this directory rather than in your home\n"
251 portable_option(const char *arg
, const char *appimage_path
, const char *name
)
254 sprintf(option
, "appimage-portable-%s", name
);
256 if (arg
&& strcmp(arg
, option
)==0) {
257 char portable_dir
[PATH_MAX
];
258 char fullpath
[PATH_MAX
];
260 ssize_t length
= readlink(appimage_path
, fullpath
, sizeof(fullpath
));
262 fprintf(stderr
, "Error getting realpath for %s\n", appimage_path
);
265 fullpath
[length
] = '\0';
267 sprintf(portable_dir
, "%s.%s", fullpath
, name
);
268 if (!mkdir(portable_dir
, S_IRWXU
))
269 fprintf(stderr
, "Portable %s directory created at %s\n", name
, portable_dir
);
271 fprintf(stderr
, "Error creating portable %s directory at %s: %s\n", name
, portable_dir
, strerror(errno
));
277 bool extract_appimage(const char* const appimage_path
, const char* const _prefix
, const char* const _pattern
, const bool overwrite
, const bool verbose
) {
278 sqfs_err err
= SQFS_OK
;
281 char prefixed_path_to_extract
[1024];
283 // local copy we can modify safely
284 // allocate 1 more byte than we would need so we can add a trailing slash if there is none yet
285 char* prefix
= malloc(strlen(_prefix
) + 2);
286 strcpy(prefix
, _prefix
);
289 if (prefix
[strlen(prefix
) - 1] != '/')
292 if (access(prefix
, F_OK
) == -1) {
293 if (mkdir_p(prefix
) == -1) {
294 perror("mkdir_p error");
299 if ((err
= sqfs_open_image(&fs
, appimage_path
, (size_t) fs_offset
))) {
300 fprintf(stderr
, "Failed to open squashfs image\n");
304 // track duplicate inodes for hardlinks
305 char** created_inode
= calloc(fs
.sb
.inodes
, sizeof(char*));
306 if (created_inode
== NULL
) {
307 fprintf(stderr
, "Failed allocating memory to track hardlinks\n");
311 if ((err
= sqfs_traverse_open(&trv
, &fs
, sqfs_inode_root(&fs
)))) {
312 fprintf(stderr
, "sqfs_traverse_open error\n");
319 while (sqfs_traverse_next(&trv
, &err
)) {
321 if (_pattern
== NULL
|| fnmatch(_pattern
, trv
.path
, FNM_FILE_NAME
| FNM_LEADING_DIR
) == 0) {
322 // fprintf(stderr, "trv.path: %s\n", trv.path);
323 // fprintf(stderr, "sqfs_inode_id: %lu\n", trv.entry.inode);
325 if (sqfs_inode_get(&fs
, &inode
, trv
.entry
.inode
)) {
326 fprintf(stderr
, "sqfs_inode_get error\n");
330 // fprintf(stderr, "inode.base.inode_type: %i\n", inode.base.inode_type);
331 // fprintf(stderr, "inode.xtra.reg.file_size: %lu\n", inode.xtra.reg.file_size);
332 strcpy(prefixed_path_to_extract
, "");
333 strcat(strcat(prefixed_path_to_extract
, prefix
), trv
.path
);
336 fprintf(stdout
, "%s\n", prefixed_path_to_extract
);
338 if (inode
.base
.inode_type
== SQUASHFS_DIR_TYPE
|| inode
.base
.inode_type
== SQUASHFS_LDIR_TYPE
) {
339 // fprintf(stderr, "inode.xtra.dir.parent_inode: %ui\n", inode.xtra.dir.parent_inode);
340 // fprintf(stderr, "mkdir_p: %s/\n", prefixed_path_to_extract);
341 if (access(prefixed_path_to_extract
, F_OK
) == -1) {
342 if (mkdir_p(prefixed_path_to_extract
) == -1) {
343 perror("mkdir_p error");
348 } else if (inode
.base
.inode_type
== SQUASHFS_REG_TYPE
|| inode
.base
.inode_type
== SQUASHFS_LREG_TYPE
) {
349 // if we've already created this inode, then this is a hardlink
350 char* existing_path_for_inode
= created_inode
[inode
.base
.inode_number
- 1];
351 if (existing_path_for_inode
!= NULL
) {
352 unlink(prefixed_path_to_extract
);
353 if (link(existing_path_for_inode
, prefixed_path_to_extract
) == -1) {
354 fprintf(stderr
, "Couldn't create hardlink from \"%s\" to \"%s\": %s\n",
355 prefixed_path_to_extract
, existing_path_for_inode
, strerror(errno
));
363 if (!overwrite
&& stat(prefixed_path_to_extract
, &st
) == 0 && st
.st_size
== inode
.xtra
.reg
.file_size
) {
364 fprintf(stderr
, "File exists and file size matches, skipping\n");
368 // track the path we extract to for this inode, so that we can `link` if this inode is found again
369 created_inode
[inode
.base
.inode_number
- 1] = strdup(prefixed_path_to_extract
);
370 // fprintf(stderr, "Extract to: %s\n", prefixed_path_to_extract);
371 if (private_sqfs_stat(&fs
, &inode
, &st
) != 0)
372 die("private_sqfs_stat error");
375 char* p
= strrchr(prefixed_path_to_extract
, '/');
377 // set an \0 to end the split the string
379 mkdir_p(prefixed_path_to_extract
);
381 // restore dir seprator
385 // Read the file in chunks
386 off_t bytes_already_read
= 0;
387 sqfs_off_t bytes_at_a_time
= 64 * 1024;
389 f
= fopen(prefixed_path_to_extract
, "w+");
391 perror("fopen error");
395 while (bytes_already_read
< inode
.xtra
.reg
.file_size
) {
396 char buf
[bytes_at_a_time
];
397 if (sqfs_read_range(&fs
, &inode
, (sqfs_off_t
) bytes_already_read
, &bytes_at_a_time
, buf
)) {
398 perror("sqfs_read_range error");
402 // fwrite(buf, 1, bytes_at_a_time, stdout);
403 fwrite(buf
, 1, bytes_at_a_time
, f
);
404 bytes_already_read
= bytes_already_read
+ bytes_at_a_time
;
407 chmod(prefixed_path_to_extract
, st
.st_mode
);
411 } else if (inode
.base
.inode_type
== SQUASHFS_SYMLINK_TYPE
|| inode
.base
.inode_type
== SQUASHFS_LSYMLINK_TYPE
) {
413 sqfs_readlink(&fs
, &inode
, NULL
, &size
);
415 int ret
= sqfs_readlink(&fs
, &inode
, buf
, &size
);
417 perror("symlink error");
421 // fprintf(stderr, "Symlink: %s to %s \n", prefixed_path_to_extract, buf);
422 unlink(prefixed_path_to_extract
);
423 ret
= symlink(buf
, prefixed_path_to_extract
);
425 fprintf(stderr
, "WARNING: could not create symlink\n");
427 fprintf(stderr
, "TODO: Implement inode.base.inode_type %i\n", inode
.base
.inode_type
);
429 // fprintf(stderr, "\n");
436 for (int i
= 0; i
< fs
.sb
.inodes
; i
++) {
437 free(created_inode
[i
]);
441 if (err
!= SQFS_OK
) {
442 fprintf(stderr
, "sqfs_traverse_next error\n");
445 sqfs_traverse_close(&trv
);
446 sqfs_fd_close(fs
.fd
);
451 int rm_recursive_callback(const char* path
, const struct stat
* stat
, const int type
, struct FTW
* ftw
) {
458 fprintf(stderr
, "%s: ftw error: %s\n",
459 path
, strerror(errno
));
463 // ignore directories at first, will be handled by FTW_DP
469 if (remove(path
) != 0) {
470 fprintf(stderr
, "Failed to remove %s: %s\n", path
, strerror(errno
));
477 if (rmdir(path
) != 0) {
478 fprintf(stderr
, "Failed to remove directory %s: %s\n", path
, strerror(errno
));
484 fprintf(stderr
, "Unexpected fts_info\n");
491 bool rm_recursive(const char* const path
) {
492 // FTW_DEPTH: perform depth-first search to make sure files are deleted before the containing directories
493 // FTW_MOUNT: prevent deletion of files on other mounted filesystems
494 // FTW_PHYS: do not follow symlinks, but report symlinks as such; this way, the symlink targets, which might point
495 // to locations outside path will not be deleted accidentally (attackers might abuse this)
496 int rv
= nftw(path
, &rm_recursive_callback
, 0, FTW_DEPTH
| FTW_MOUNT
| FTW_PHYS
);
501 bool build_mount_point(char* mount_dir
, const char* const argv0
, char const* const temp_base
, const size_t templen
) {
502 const size_t maxnamelen
= 6;
504 // when running for another AppImage, we should use that for building the mountpoint name instead
505 char* target_appimage
= getenv("TARGET_APPIMAGE");
508 if (target_appimage
!= NULL
) {
509 path_basename
= basename(target_appimage
);
511 path_basename
= basename(argv0
);
514 size_t namelen
= strlen(path_basename
);
515 // limit length of tempdir name
516 if (namelen
> maxnamelen
) {
517 namelen
= maxnamelen
;
520 strcpy(mount_dir
, temp_base
);
521 strncpy(mount_dir
+ templen
, "/.mount_", 8);
522 strncpy(mount_dir
+ templen
+ 8, path_basename
, namelen
);
523 strncpy(mount_dir
+templen
+8+namelen
, "XXXXXX", 6);
524 mount_dir
[templen
+8+namelen
+6] = 0; // null terminate destination
527 void set_portable_home_and_config(char *basepath
) {
528 char portable_home_dir
[PATH_MAX
];
529 char portable_config_dir
[PATH_MAX
];
531 /* If there is a directory with the same name as the AppImage plus ".home", then export $HOME */
532 strcpy (portable_home_dir
, basepath
);
533 strcat (portable_home_dir
, ".home");
534 if(is_writable_directory(portable_home_dir
)){
535 fprintf(stderr
, "Setting $HOME to %s\n", portable_home_dir
);
536 setenv("HOME",portable_home_dir
,1);
539 /* If there is a directory with the same name as the AppImage plus ".config", then export $XDG_CONFIG_HOME */
540 strcpy (portable_config_dir
, basepath
);
541 strcat (portable_config_dir
, ".config");
542 if(is_writable_directory(portable_config_dir
)){
543 fprintf(stderr
, "Setting $XDG_CONFIG_HOME to %s\n", portable_config_dir
);
544 setenv("XDG_CONFIG_HOME",portable_config_dir
,1);
548 int main(int argc
, char *argv
[]) {
549 char appimage_path
[PATH_MAX
];
550 char argv0_path
[PATH_MAX
];
553 /* We might want to operate on a target appimage rather than this file itself,
554 * e.g., for appimaged which must not run untrusted code from random AppImages.
555 * This variable is intended for use by e.g., appimaged and is subject to
556 * change any time. Do not rely on it being present. We might even limit this
557 * functionality specifically for builds used by appimaged.
559 if (getenv("TARGET_APPIMAGE") == NULL
) {
560 strcpy(appimage_path
, "/proc/self/exe");
561 strcpy(argv0_path
, argv
[0]);
563 strcpy(appimage_path
, getenv("TARGET_APPIMAGE"));
564 strcpy(argv0_path
, getenv("TARGET_APPIMAGE"));
566 #ifdef ENABLE_SETPROCTITLE
567 // load libbsd dynamically to change proc title
568 // this is an optional feature, therefore we don't hard require it
569 void* libbsd
= dlopen("libbsd.so", RTLD_NOW
);
571 if (libbsd
!= NULL
) {
575 // try to load the two required symbols
576 void (*setproctitle_init
)(int, char**, char**) = dlsym(libbsd
, "setproctitle_init");
580 if ((error
= dlerror()) == NULL
) {
581 void (*setproctitle
)(const char*, char*) = dlsym(libbsd
, "setproctitle");
583 if (dlerror() == NULL
) {
585 strcpy(buffer
, getenv("TARGET_APPIMAGE"));
586 for (int i
= 1; i
< argc
; i
++) {
588 strcat(buffer
, argv
[i
]);
591 (*setproctitle_init
)(argc
, argv
, environ
);
592 (*setproctitle
)("%s", buffer
);
601 // temporary directories are required in a few places
602 // therefore we implement the detection of the temp base dir at the top of the code to avoid redundancy
603 char temp_base
[PATH_MAX
] = P_tmpdir
;
606 const char* const TMPDIR
= getenv("TMPDIR");
608 strcpy(temp_base
, getenv("TMPDIR"));
611 fs_offset
= appimage_get_elf_size(appimage_path
);
615 fprintf(stderr
, "Failed to get fs offset for %s\n", appimage_path
);
616 exit(EXIT_EXECERROR
);
619 arg
=getArg(argc
,argv
,'-');
621 /* Print the help and then exit */
622 if(arg
&& strcmp(arg
,"appimage-help")==0) {
623 char fullpath
[PATH_MAX
];
625 ssize_t length
= readlink(appimage_path
, fullpath
, sizeof(fullpath
));
627 fprintf(stderr
, "Error getting realpath for %s\n", appimage_path
);
628 exit(EXIT_EXECERROR
);
630 fullpath
[length
] = '\0';
632 print_help(fullpath
);
636 /* Just print the offset and then exit */
637 if(arg
&& strcmp(arg
,"appimage-offset")==0) {
638 printf("%lu\n", fs_offset
);
642 arg
=getArg(argc
,argv
,'-');
644 /* extract the AppImage */
645 if(arg
&& strcmp(arg
,"appimage-extract")==0) {
648 // default use case: use standard prefix
651 } else if (argc
== 3) {
654 fprintf(stderr
, "Unexpected argument count: %d\n", argc
- 1);
655 fprintf(stderr
, "Usage: %s --appimage-extract [<prefix>]\n", argv0_path
);
659 if (!extract_appimage(appimage_path
, "squashfs-root/", pattern
, true, true)) {
666 // calculate full path of AppImage
668 char fullpath
[PATH_MAX
];
670 if(getenv("TARGET_APPIMAGE") == NULL
) {
671 // If we are operating on this file itself
672 ssize_t len
= readlink(appimage_path
, fullpath
, sizeof(fullpath
));
674 perror("Failed to obtain absolute path");
675 exit(EXIT_EXECERROR
);
677 fullpath
[len
] = '\0';
679 char* abspath
= realpath(appimage_path
, NULL
);
680 if (abspath
== NULL
) {
681 perror("Failed to obtain absolute path");
682 exit(EXIT_EXECERROR
);
684 strcpy(fullpath
, abspath
);
688 if (getenv("APPIMAGE_EXTRACT_AND_RUN") != NULL
|| (arg
&& strcmp(arg
, "appimage-extract-and-run") == 0)) {
689 char* hexlified_digest
= NULL
;
691 // calculate MD5 hash of file, and use it to make extracted directory name "content-aware"
692 // see https://github.com/AppImage/AppImageKit/issues/841 for more information
694 FILE* f
= fopen(appimage_path
, "rb");
696 perror("Failed to open AppImage file");
697 exit(EXIT_EXECERROR
);
704 for (size_t bytes_read
; (bytes_read
= fread(buf
, sizeof(char), sizeof(buf
), f
)); bytes_read
> 0) {
705 Md5Update(&ctx
, buf
, (uint32_t) bytes_read
);
709 Md5Finalise(&ctx
, &digest
);
711 hexlified_digest
= appimage_hexlify(digest
.bytes
, sizeof(digest
.bytes
));
714 char* prefix
= malloc(strlen(temp_base
) + 20 + strlen(hexlified_digest
) + 2);
715 strcpy(prefix
, temp_base
);
716 strcat(prefix
, "/appimage_extracted_");
717 strcat(prefix
, hexlified_digest
);
718 free(hexlified_digest
);
720 const bool verbose
= (getenv("VERBOSE") != NULL
);
722 if (!extract_appimage(appimage_path
, prefix
, NULL
, false, verbose
)) {
723 fprintf(stderr
, "Failed to extract AppImage\n");
724 exit(EXIT_EXECERROR
);
728 if ((pid
= fork()) == -1) {
730 fprintf(stderr
, "fork() failed: %s\n", strerror(error
));
731 exit(EXIT_EXECERROR
);
732 } else if (pid
== 0) {
733 const char apprun_fname
[] = "AppRun";
734 char* apprun_path
= malloc(strlen(prefix
) + 1 + strlen(apprun_fname
) + 1);
735 strcpy(apprun_path
, prefix
);
736 strcat(apprun_path
, "/");
737 strcat(apprun_path
, apprun_fname
);
739 // create copy of argument list without the --appimage-extract-and-run parameter
740 char* new_argv
[argc
];
742 new_argv
[new_argc
++] = strdup(apprun_path
);
743 for (int i
= 1; i
< argc
; ++i
) {
744 if (strcmp(argv
[i
], "--appimage-extract-and-run") != 0) {
745 new_argv
[new_argc
++] = strdup(argv
[i
]);
748 new_argv
[new_argc
] = NULL
;
750 /* Setting some environment variables that the app "inside" might use */
751 setenv("APPIMAGE", fullpath
, 1);
752 setenv("ARGV0", argv0_path
, 1);
753 setenv("APPDIR", prefix
, 1);
755 set_portable_home_and_config(fullpath
);
757 execv(apprun_path
, new_argv
);
760 fprintf(stderr
, "Failed to run %s: %s\n", apprun_path
, strerror(error
));
763 exit(EXIT_EXECERROR
);
767 int rv
= waitpid(pid
, &status
, 0);
768 status
= rv
> 0 && WIFEXITED (status
) ? WEXITSTATUS (status
) : EXIT_EXECERROR
;
770 if (getenv("NO_CLEANUP") == NULL
) {
771 if (!rm_recursive(prefix
)) {
772 fprintf(stderr
, "Failed to clean up cache directory\n");
773 if (status
== 0) /* avoid messing existing failure exit status */
774 status
= EXIT_EXECERROR
;
778 // template == prefix, must be freed only once
784 if(arg
&& strcmp(arg
,"appimage-version")==0) {
785 fprintf(stderr
,"Version: %s\n", GIT_COMMIT
);
789 if(arg
&& (strcmp(arg
,"appimage-updateinformation")==0 || strcmp(arg
,"appimage-updateinfo")==0)) {
790 unsigned long offset
= 0;
791 unsigned long length
= 0;
792 appimage_get_elf_section_offset_and_length(appimage_path
, ".upd_info", &offset
, &length
);
793 // fprintf(stderr, "offset: %lu\n", offset);
794 // fprintf(stderr, "length: %lu\n", length);
795 // print_hex(appimage_path, offset, length);
796 appimage_print_binary(appimage_path
, offset
, length
);
800 if(arg
&& strcmp(arg
,"appimage-signature")==0) {
801 unsigned long offset
= 0;
802 unsigned long length
= 0;
803 appimage_get_elf_section_offset_and_length(appimage_path
, ".sha256_sig", &offset
, &length
);
804 // fprintf(stderr, "offset: %lu\n", offset);
805 // fprintf(stderr, "length: %lu\n", length);
806 // print_hex(appimage_path, offset, length);
807 appimage_print_binary(appimage_path
, offset
, length
);
811 portable_option(arg
, appimage_path
, "home");
812 portable_option(arg
, appimage_path
, "config");
814 // If there is an argument starting with appimage- (but not appimage-mount which is handled further down)
815 // then stop here and print an error message
816 if((arg
&& strncmp(arg
, "appimage-", 8) == 0) && (arg
&& strcmp(arg
,"appimage-mount")!=0)) {
817 fprintf(stderr
,"--%s is not yet implemented in version %s\n", arg
, GIT_COMMIT
);
821 LOAD_LIBRARY
; /* exit if libfuse is missing */
825 size_t templen
= strlen(temp_base
);
827 // allocate enough memory (size of name won't exceed 60 bytes)
828 char mount_dir
[templen
+ 60];
830 build_mount_point(mount_dir
, argv
[0], temp_base
, templen
);
832 size_t mount_dir_size
= strlen(mount_dir
);
837 if (mkdtemp(mount_dir
) == NULL
) {
838 perror ("create mount dir error");
839 exit (EXIT_EXECERROR
);
842 if (pipe (keepalive_pipe
) == -1) {
843 perror ("pipe error");
844 exit (EXIT_EXECERROR
);
849 perror ("fork error");
850 exit (EXIT_EXECERROR
);
858 /* close read pipe */
859 close (keepalive_pipe
[0]);
861 char *dir
= realpath(appimage_path
, NULL
);
864 sprintf(options
, "ro,offset=%lu", fs_offset
);
867 child_argv
[1] = "-o";
868 child_argv
[2] = options
;
870 child_argv
[4] = mount_dir
;
872 if(0 != fusefs_main (5, child_argv
, fuse_mounted
)){
875 title
= "Cannot mount AppImage, please check your FUSE setup.";
876 body
= "You might still be able to extract the contents of this AppImage \n"
877 "if you run it with the --appimage-extract option. \n"
878 "See https://github.com/AppImage/AppImageKit/wiki/FUSE \n"
879 "for more information";
880 notify(title
, body
, 0); // 3 seconds timeout
883 /* in parent, child is $pid */
886 /* close write pipe */
887 close (keepalive_pipe
[1]);
889 /* Pause until mounted */
890 read (keepalive_pipe
[0], &c
, 1);
892 /* Fuse process has now daemonized, reap our child */
893 waitpid(pid
, NULL
, 0);
895 dir_fd
= open (mount_dir
, O_RDONLY
);
897 perror ("open dir error");
898 exit (EXIT_EXECERROR
);
901 res
= dup2 (dir_fd
, 1023);
903 perror ("dup2 error");
904 exit (EXIT_EXECERROR
);
908 real_argv
= malloc (sizeof (char *) * (argc
+ 1));
909 for (i
= 0; i
< argc
; i
++) {
910 real_argv
[i
] = argv
[i
];
914 if(arg
&& strcmp(arg
, "appimage-mount") == 0) {
915 char real_mount_dir
[PATH_MAX
];
917 if (realpath(mount_dir
, real_mount_dir
) == real_mount_dir
) {
918 printf("%s\n", real_mount_dir
);
920 printf("%s\n", mount_dir
);
923 // stdout is, by default, buffered (unlike stderr), therefore in order to allow other processes to read
924 // the path from stdout, we need to flush the buffers now
925 // this is a less-invasive alternative to setbuf(stdout, NULL);
933 /* Setting some environment variables that the app "inside" might use */
934 setenv( "APPIMAGE", fullpath
, 1 );
935 setenv( "ARGV0", argv0_path
, 1 );
936 setenv( "APPDIR", mount_dir
, 1 );
938 set_portable_home_and_config(fullpath
);
940 /* Original working directory */
942 if (getcwd(cwd
, sizeof(cwd
)) != NULL
) {
943 setenv( "OWD", cwd
, 1 );
946 char filename
[mount_dir_size
+ 8]; /* enough for mount_dir + "/AppRun" */
947 strcpy (filename
, mount_dir
);
948 strcat (filename
, "/AppRun");
950 /* TODO: Find a way to get the exit status and/or output of this */
951 execv (filename
, real_argv
);
952 /* Error if we continue here */
953 perror("execv error");
954 exit(EXIT_EXECERROR
);