1 /**************************************************************************
3 * Copyright (c) 2004-17 Simon Peter
7 * Permission is hereby granted, free of charge, to any person obtaining a copy
8 * of this software and associated documentation files (the "Software"), to deal
9 * in the Software without restriction, including without limitation the rights
10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 * copies of the Software, and to permit persons to whom the Software is
12 * furnished to do so, subject to the following conditions:
14 * The above copyright notice and this permission notice shall be included in
15 * all copies or substantial portions of the Software.
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 **************************************************************************/
27 #ident "AppImage by Simon Peter, http://appimage.org/"
30 #include <glib/gstdio.h>
38 #include "squashfuse.h"
40 #include <sys/types.h>
52 #include "getsection.h"
54 extern int _binary_runtime_start
;
55 extern int _binary_runtime_end
;
57 static gchar
const APPIMAGEIGNORE
[] = ".appimageignore";
58 static char _exclude_file_desc
[256];
60 static gboolean list
= FALSE
;
61 static gboolean verbose
= FALSE
;
62 static gboolean version
= FALSE
;
63 static gboolean sign
= FALSE
;
64 static gboolean no_appstream
= FALSE
;
65 gchar
**remaining_args
= NULL
;
66 gchar
*updateinformation
= NULL
;
67 gchar
*bintray_user
= NULL
;
68 gchar
*bintray_repo
= NULL
;
69 gchar
*sqfs_comp
= "gzip";
70 gchar
*exclude_file
= NULL
;
72 // #####################################################################
74 static void die(const char *msg
) {
75 fprintf(stderr
, "%s\n", msg
);
79 /* Function that prints the contents of a squashfs file
80 * using libsquashfuse (#include "squashfuse.h") */
81 int sfs_ls(char* image
) {
82 sqfs_err err
= SQFS_OK
;
86 unsigned long fs_offset
= get_elf_size(image
);
88 if ((err
= sqfs_open_image(&fs
, image
, fs_offset
)))
89 die("sqfs_open_image error");
91 if ((err
= sqfs_traverse_open(&trv
, &fs
, sqfs_inode_root(&fs
))))
92 die("sqfs_traverse_open error");
93 while (sqfs_traverse_next(&trv
, &err
)) {
95 printf("%s\n", trv
.path
);
99 die("sqfs_traverse_next error");
100 sqfs_traverse_close(&trv
);
102 sqfs_fd_close(fs
.fd
);
106 /* Generate a squashfs filesystem using mksquashfs on the $PATH
107 * execlp(), execvp(), and execvpe() search on the $PATH */
108 int sfs_mksquashfs(char *source
, char *destination
, int offset
) {
112 // error, failed to fork()
114 } else if (pid
> 0) {
116 waitpid(pid
, &status
, 0);
119 gchar
*offset_string
;
120 offset_string
= g_strdup_printf("%i", offset
);
123 bool use_xz
= strcmp(sqfs_comp
, "xz") >= 0;
126 args
[i
++] = "mksquashfs";
128 args
[i
++] = destination
;
129 args
[i
++] = "-offset";
130 args
[i
++] = offset_string
;
136 args
[i
++] = sqfs_comp
;
138 args
[i
++] = "-root-owned";
139 args
[i
++] = "-noappend";
142 // https://jonathancarter.org/2015/04/06/squashfs-performance-testing/ says:
143 // improved performance by using a 16384 block size with a sacrifice of around 3% more squashfs image space
144 args
[i
++] = "-Xdict-size";
150 // check if ignore file exists and use it if possible
151 if(access(APPIMAGEIGNORE
, F_OK
) >= 0) {
152 printf("Including %s", APPIMAGEIGNORE
);
153 args
[i
++] = "-wildcards";
156 // avoid warning: assignment discards ‘const’ qualifier
158 strcpy(buf
, APPIMAGEIGNORE
);
162 // if an exclude file has been passed on the command line, should be used, too
163 if(exclude_file
!= 0 && strlen(exclude_file
) > 0) {
164 if(access(exclude_file
, F_OK
) < 0) {
165 printf("WARNING: exclude file %s not found!", exclude_file
);
169 args
[i
++] = "-wildcards";
171 args
[i
++] = exclude_file
;
176 execvp("mksquashfs", args
);
178 perror("execlp"); // exec*() returns only on error
179 return -1; // exec never returns
184 /* Validate desktop file using desktop-file-validate on the $PATH
185 * execlp(), execvp(), and execvpe() search on the $PATH */
186 int validate_desktop_file(char *file
) {
192 printf("could not fork! \n");
195 else if(child_pid
== 0)
197 execlp("desktop-file-validate", "desktop-file-validate", file
, NULL
);
201 waitpid(child_pid
, &statval
, WUNTRACED
| WCONTINUED
);
202 if(WIFEXITED(statval
)){
203 return(WEXITSTATUS(statval
));
209 /* Generate a squashfs filesystem
210 * The following would work if we link to mksquashfs.o after we renamed
211 * main() to mksquashfs_main() in mksquashfs.c but we don't want to actually do
212 * this because squashfs-tools is not under a permissive license
213 * i *nt sfs_mksquashfs(char *source, char *destination) {
214 * char *child_argv[5];
215 * child_argv[0] = NULL;
216 * child_argv[1] = source;
217 * child_argv[2] = destination;
218 * child_argv[3] = "-root-owned";
219 * child_argv[4] = "-noappend";
220 * mksquashfs_main(5, child_argv);
224 gchar
* find_first_matching_file(const gchar
*real_path
, const gchar
*pattern
) {
228 dir
= g_dir_open(real_path
, 0, NULL
);
231 while ((entry
= g_dir_read_name(dir
)) != NULL
) {
232 full_name
= g_build_filename(real_path
, entry
, NULL
);
233 if (! g_file_test(full_name
, G_FILE_TEST_IS_DIR
)) {
234 if(g_pattern_match_simple(pattern
, entry
))
238 resulting
= find_first_matching_file(full_name
, pattern
);
246 g_warning("%s: %s", real_path
, g_strerror(errno
));
251 gchar
* find_first_matching_file_nonrecursive(const gchar
*real_path
, const gchar
*pattern
) {
254 dir
= g_dir_open(real_path
, 0, NULL
);
257 while ((entry
= g_dir_read_name(dir
)) != NULL
) {
258 full_name
= g_build_filename(real_path
, entry
, NULL
);
259 if (g_file_test(full_name
, G_FILE_TEST_IS_REGULAR
)) {
260 if(g_pattern_match_simple(pattern
, entry
))
267 g_warning("%s: %s", real_path
, g_strerror(errno
));
272 gchar
* get_desktop_entry(GKeyFile
*kf
, char *key
) {
273 gchar
*value
= g_key_file_get_string (kf
, "Desktop Entry", key
, NULL
);
275 fprintf(stderr
, "%s entry not found in desktop file\n", key
);
280 /* in-place modification of the string, and assuming the buffer pointed to by
281 * line is large enough to hold the resulting string*/
282 static void replacestr(char *line
, const char *search
, const char *replace
)
286 if ((sp
= strstr(line
, search
)) == NULL
) {
289 int search_len
= strlen(search
);
290 int replace_len
= strlen(replace
);
291 int tail_len
= strlen(sp
+search_len
);
293 memmove(sp
+replace_len
,sp
+search_len
,tail_len
+1);
294 memcpy(sp
, replace
, replace_len
);
296 /* Do it recursively again until no more work to do */
298 if ((sp
= strstr(line
, search
))) {
299 replacestr(line
, search
, replace
);
303 // #####################################################################
305 static GOptionEntry entries
[] =
307 { "list", 'l', 0, G_OPTION_ARG_NONE
, &list
, "List files in SOURCE AppImage", NULL
},
308 { "updateinformation", 'u', 0, G_OPTION_ARG_STRING
, &updateinformation
, "Embed update information STRING; if zsyncmake is installed, generate zsync file", NULL
},
309 { "bintray-user", 0, 0, G_OPTION_ARG_STRING
, &bintray_user
, "Bintray user name", NULL
},
310 { "bintray-repo", 0, 0, G_OPTION_ARG_STRING
, &bintray_repo
, "Bintray repository", NULL
},
311 { "version", 0, 0, G_OPTION_ARG_NONE
, &version
, "Show version number", NULL
},
312 { "verbose", 'v', 0, G_OPTION_ARG_NONE
, &verbose
, "Produce verbose output", NULL
},
313 { "sign", 's', 0, G_OPTION_ARG_NONE
, &sign
, "Sign with gpg2", NULL
},
314 { "comp", 0, 0, G_OPTION_ARG_STRING
, &sqfs_comp
, "Squashfs compression", NULL
},
315 { "no-appstream", 'n', 0, G_OPTION_ARG_NONE
, &no_appstream
, "Do not check AppStream metadata", NULL
},
316 { "exclude-file", 0, 0, G_OPTION_ARG_STRING
, &exclude_file
, _exclude_file_desc
, NULL
},
317 { G_OPTION_REMAINING
, 0, 0, G_OPTION_ARG_FILENAME_ARRAY
, &remaining_args
, NULL
, NULL
},
322 main (int argc
, char *argv
[])
324 /* Parse VERSION environment variable.
325 * We cannot use g_environ_getenv (g_get_environ() since it is too new for CentOS 6 */
327 version_env
= getenv("VERSION");
329 /* Parse OWD environment variable.
330 * If it is available then cd there. It is the original CWD prior to running AppRun */
331 char* owd_env
= NULL
;
332 owd_env
= getenv("OWD");
335 ret
= chdir(owd_env
);
337 fprintf(stderr
, "Could not cd into %s\n", owd_env
);
343 GError
*error
= NULL
;
344 GOptionContext
*context
;
345 char command
[PATH_MAX
];
347 // initialize help text of argument
348 sprintf(_exclude_file_desc
, "Uses given file as exclude file for mksquashfs, in addition to %s.", APPIMAGEIGNORE
);
350 context
= g_option_context_new ("SOURCE [DESTINATION] - Generate, extract, and inspect AppImages");
351 g_option_context_add_main_entries (context
, entries
, NULL
);
352 // g_option_context_add_group (context, gtk_get_option_group (TRUE));
353 if (!g_option_context_parse (context
, &argc
, &argv
, &error
))
355 fprintf(stderr
, "Option parsing failed: %s\n", error
->message
);
360 fprintf(stderr
,"Version: %s\n", VERSION_NUMBER
);
364 if(!((0 == strcmp(sqfs_comp
, "gzip")) || (0 ==strcmp(sqfs_comp
, "xz"))))
365 die("Only gzip (faster execution, larger files) and xz (slower execution, smaller files) compression is supported at the moment. Let us know if there are reasons for more, should be easy to add. You could help the project by doing some systematic size/performance measurements. Watch for size, execution speed, and zsync delta size.");
366 /* Check for dependencies here. Better fail early if they are not present. */
367 if(! g_find_program_in_path ("mksquashfs"))
368 die("mksquashfs is missing but required, please install it");
369 if(! g_find_program_in_path ("desktop-file-validate"))
370 g_print("WARNING: desktop-file-validate is missing, please install it so that desktop files can be checked for potential errors\n");
371 if(! g_find_program_in_path ("zsyncmake"))
372 g_print("WARNING: zsyncmake is missing, please install it if you want to use binary delta updates\n");
374 if(! g_find_program_in_path ("appstreamcli"))
375 g_print("WARNING: appstreamcli is missing, please install it if you want to use AppStream metadata\n");
376 if(! g_find_program_in_path ("gpg2"))
377 g_print("WARNING: gpg2 is missing, please install it if you want to create digital signatures\n");
378 if(! g_find_program_in_path ("sha256sum"))
379 g_print("WARNING: sha256sum is missing, please install it if you want to create digital signatures\n");
381 if(!&remaining_args
[0])
382 die("SOURCE is missing");
384 /* If in list mode */
386 sfs_ls(remaining_args
[0]);
390 /* If the first argument is a directory, then we assume that we should package it */
391 if (g_file_test (remaining_args
[0], G_FILE_TEST_IS_DIR
)){
393 char source
[PATH_MAX
];
394 realpath(remaining_args
[0], source
);
396 /* Check if *.desktop file is present in source AppDir */
397 gchar
*desktop_file
= find_first_matching_file_nonrecursive(source
, "*.desktop");
398 if(desktop_file
== NULL
){
399 die("Desktop file not found, aborting");
402 fprintf (stdout
, "Desktop file: %s\n", desktop_file
);
404 if(g_find_program_in_path ("desktop-file-validate")) {
405 if(validate_desktop_file(desktop_file
) != 0){
406 fprintf(stderr
, "ERROR: Desktop file contains errors. Please fix them. Please see\n");
407 fprintf(stderr
, " https://standards.freedesktop.org/desktop-entry-spec/latest/\n");
408 die(" for more information.");
412 /* Read information from .desktop file */
413 GKeyFile
*kf
= g_key_file_new ();
414 if (!g_key_file_load_from_file (kf
, desktop_file
, 0, NULL
))
415 die(".desktop file cannot be parsed");
418 fprintf (stderr
,"Name: %s\n", get_desktop_entry(kf
, "Name"));
419 fprintf (stderr
,"Icon: %s\n", get_desktop_entry(kf
, "Icon"));
420 fprintf (stderr
,"Exec: %s\n", get_desktop_entry(kf
, "Exec"));
421 fprintf (stderr
,"Comment: %s\n", get_desktop_entry(kf
, "Comment"));
422 fprintf (stderr
,"Type: %s\n", get_desktop_entry(kf
, "Type"));
423 fprintf (stderr
,"Categories: %s\n", get_desktop_entry(kf
, "Categories"));
426 /* Determine the architecture */
427 gchar
*arch
= getenv("ARCH");
429 /* If no $ARCH variable is set check a file */
431 gchar
*archfile
= NULL
;
432 /* We use the next best .so that we can find to determine the architecture */
433 archfile
= find_first_matching_file(source
, "*.so.*");
436 /* If we found no .so we try to guess the main executable - this might be a script though */
437 // char guessed_bin_path[PATH_MAX];
438 // sprintf (guessed_bin_path, "%s/usr/bin/%s", source, g_strsplit_set(get_desktop_entry(kf, "Exec"), " ", -1)[0]);
439 // archfile = guessed_bin_path;
440 archfile
= "/proc/self/exe";
443 fprintf (stderr
,"File used for determining architecture: %s\n", archfile
);
446 char command
[PATH_MAX
];
447 sprintf (command
, "/usr/bin/file -L -N -b %s", archfile
);
448 fp
= popen(command
, "r");
450 die("Failed to run file command");
451 fgets(line
, sizeof(line
)-1, fp
);
452 arch
= g_strstrip(g_strsplit_set(line
, ",", -1)[1]);
453 replacestr(arch
, "-", "_");
454 fprintf (stderr
,"Arch: %s\n", arch
+1);
459 printf("The architecture could not be determined, assuming 'all'\n");
464 char app_name_for_filename
[PATH_MAX
];
465 sprintf(app_name_for_filename
, "%s", get_desktop_entry(kf
, "Name"));
466 replacestr(app_name_for_filename
, " ", "_");
469 fprintf (stderr
,"App name for filename: %s\n", app_name_for_filename
);
471 if (remaining_args
[1]) {
472 destination
= remaining_args
[1];
474 /* No destination has been specified, to let's construct one
475 * TODO: Find out the architecture and use a $VERSION that might be around in the env */
476 char dest_path
[PATH_MAX
];
477 sprintf (dest_path
, "%s-%s.AppImage", app_name_for_filename
, arch
);
480 fprintf (stderr
,"dest_path: %s\n", dest_path
);
482 if (version_env
!=NULL
)
483 sprintf (dest_path
, "%s-%s-%s.AppImage", app_name_for_filename
, version_env
, arch
);
485 destination
= dest_path
;
486 replacestr(destination
, " ", "_");
488 fprintf (stdout
, "%s should be packaged as %s\n", source
, destination
);
489 /* Check if the Icon file is how it is expected */
490 gchar
* icon_name
= get_desktop_entry(kf
, "Icon");
491 gchar
* icon_file_path
= NULL
;
492 gchar
* icon_file_png
;
493 gchar
* icon_file_svg
;
494 gchar
* icon_file_svgz
;
495 gchar
* icon_file_xpm
;
496 icon_file_png
= g_strdup_printf("%s/%s.png", source
, icon_name
);
497 icon_file_svg
= g_strdup_printf("%s/%s.svg", source
, icon_name
);
498 icon_file_svgz
= g_strdup_printf("%s/%s.svgz", source
, icon_name
);
499 icon_file_xpm
= g_strdup_printf("%s/%s.xpm", source
, icon_name
);
500 if (g_file_test(icon_file_png
, G_FILE_TEST_IS_REGULAR
)) {
501 icon_file_path
= icon_file_png
;
502 } else if(g_file_test(icon_file_svg
, G_FILE_TEST_IS_REGULAR
)) {
503 icon_file_path
= icon_file_svg
;
504 } else if(g_file_test(icon_file_svgz
, G_FILE_TEST_IS_REGULAR
)) {
505 icon_file_path
= icon_file_svgz
;
506 } else if(g_file_test(icon_file_xpm
, G_FILE_TEST_IS_REGULAR
)) {
507 icon_file_path
= icon_file_xpm
;
509 fprintf (stderr
, "%s{.png,.svg,.svgz,.xpm} not present but defined in desktop file\n", icon_name
);
513 /* Check if .DirIcon is present in source AppDir */
514 gchar
*diricon_path
= g_build_filename(source
, ".DirIcon", NULL
);
516 if (! g_file_test(diricon_path
, G_FILE_TEST_EXISTS
)){
517 fprintf (stderr
, "Deleting pre-existing .DirIcon\n");
518 g_unlink(diricon_path
);
520 if (! g_file_test(diricon_path
, G_FILE_TEST_IS_REGULAR
)){
521 fprintf (stderr
, "Creating .DirIcon symlink based on information from desktop file\n");
522 int res
= symlink(basename(icon_file_path
), diricon_path
);
524 die("Could not symlink .DirIcon");
527 /* Check if AppStream upstream metadata is present in source AppDir */
529 char application_id
[PATH_MAX
];
530 sprintf (application_id
, "%s", basename(desktop_file
));
531 replacestr(application_id
, ".desktop", ".appdata.xml");
532 gchar
*appdata_path
= g_build_filename(source
, "/usr/share/metainfo/", application_id
, NULL
);
533 if (! g_file_test(appdata_path
, G_FILE_TEST_IS_REGULAR
)){
534 fprintf (stderr
, "WARNING: AppStream upstream metadata is missing, please consider creating it\n");
535 fprintf (stderr
, " in usr/share/metainfo/%s\n", application_id
);
536 fprintf (stderr
, " Please see https://www.freedesktop.org/software/appstream/docs/chap-Quickstart.html#sect-Quickstart-DesktopApps\n");
537 fprintf (stderr
, " for more information.\n");
538 /* As a courtesy, generate one to be filled by the user */
539 if(g_find_program_in_path ("appstream-util")) {
540 gchar
*appdata_dir
= g_build_filename(source
, "/usr/share/metainfo/", NULL
);
541 g_mkdir_with_parents(appdata_dir
, 0755);
542 sprintf (command
, "%s appdata-from-desktop %s %s", g_find_program_in_path ("appstream-util"), desktop_file
, appdata_path
);
543 int ret
= system(command
);
545 die("Failed to generate AppStream template");
546 fprintf (stderr
, "AppStream template has been generated in in %s, please edit it\n", appdata_path
);
550 fprintf (stderr
, "AppStream upstream metadata found in usr/share/metainfo/%s\n", application_id
);
551 /* Use ximion's appstreamcli to make sure that desktop file and appdata match together */
552 if(g_find_program_in_path ("appstreamcli")) {
553 sprintf (command
, "%s validate-tree %s", g_find_program_in_path ("appstreamcli"), source
);
554 int ret
= system(command
);
556 die("Failed to validate AppStream information with appstreamcli");
558 /* It seems that hughsie's appstream-util does additional validations */
559 if(g_find_program_in_path ("appstream-util")) {
560 sprintf (command
, "%s validate-relax %s", g_find_program_in_path ("appstream-util"), appdata_path
);
561 int ret
= system(command
);
563 die("Failed to validate AppStream information with appstream-util");
568 /* Upstream mksquashfs can currently not start writing at an offset,
569 * so we need a patched one. https://github.com/plougher/squashfs-tools/pull/13
570 * should hopefully change that. */
572 fprintf (stderr
, "Generating squashfs...\n");
573 /* runtime is embedded into this executable
574 * http://stupefydeveloper.blogspot.de/2008/08/cc-embed-binary-data-into-elf.html */
575 int size
= (int)((void *)&_binary_runtime_end
- (void *)&_binary_runtime_start
);
576 char *data
= (char *)&_binary_runtime_start
;
578 printf("Size of the embedded runtime: %d bytes\n", size
);
580 int result
= sfs_mksquashfs(source
, destination
, size
);
582 die("sfs_mksquashfs error");
584 fprintf (stderr
, "Embedding ELF...\n");
585 FILE *fpdst
= fopen(destination
, "rb+");
587 die("Not able to open the AppImage for writing, aborting");
590 fseek(fpdst
, 0, SEEK_SET
);
591 fwrite(data
, size
, 1, fpdst
);
594 fprintf (stderr
, "Marking the AppImage as executable...\n");
595 if (chmod (destination
, 0755) < 0) {
596 printf("Could not set executable bit, aborting\n");
600 if(bintray_user
!= NULL
){
601 if(bintray_repo
!= NULL
){
603 sprintf(buf
, "bintray-zsync|%s|%s|%s|%s-_latestVersion-%s.AppImage.zsync", bintray_user
, bintray_repo
, app_name_for_filename
, app_name_for_filename
, arch
);
604 updateinformation
= buf
;
605 printf("%s\n", updateinformation
);
609 /* If updateinformation was provided, then we check and embed it */
610 if(updateinformation
!= NULL
){
611 if(!g_str_has_prefix(updateinformation
,"zsync|"))
612 if(!g_str_has_prefix(updateinformation
,"bintray-zsync|"))
613 if(!g_str_has_prefix(updateinformation
,"gh-releases-zsync|"))
614 die("The provided updateinformation is not in a recognized format");
616 gchar
**ui_type
= g_strsplit_set(updateinformation
, "|", -1);
619 printf("updateinformation type: %s\n", ui_type
[0]);
620 /* TODO: Further checking of the updateinformation */
623 unsigned long ui_offset
= 0;
624 unsigned long ui_length
= 0;
625 get_elf_section_offset_and_lenghth(destination
, ".upd_info", &ui_offset
, &ui_length
);
627 printf("ui_offset: %lu\n", ui_offset
);
628 printf("ui_length: %lu\n", ui_length
);
631 die("Could not determine offset for updateinformation");
633 if(strlen(updateinformation
)>ui_length
)
634 die("updateinformation does not fit into segment, aborting");
635 FILE *fpdst2
= fopen(destination
, "r+");
637 die("Not able to open the destination file for writing, aborting");
638 fseek(fpdst2
, ui_offset
, SEEK_SET
);
639 // fseek(fpdst2, ui_offset, SEEK_SET);
640 // fwrite(0x00, 1, 1024, fpdst); // FIXME: Segfaults; why?
641 // fseek(fpdst, ui_offset, SEEK_SET);
642 fwrite(updateinformation
, strlen(updateinformation
), 1, fpdst2
);
648 /* The user has indicated that he wants to sign */
649 gchar
*gpg2_path
= g_find_program_in_path ("gpg2");
650 gchar
*sha256sum_path
= g_find_program_in_path ("sha256sum");
652 fprintf (stderr
, "gpg2 is not installed, cannot sign\n");
654 else if(!sha256sum_path
){
655 fprintf (stderr
, "sha256sum is not installed, cannot sign\n");
657 fprintf (stderr
, "gpg2 and sha256sum are installed and user requested to sign, "
660 digestfile
= br_strcat(destination
, ".digest");
662 ascfile
= br_strcat(destination
, ".digest.asc");
663 if (g_file_test (digestfile
, G_FILE_TEST_IS_REGULAR
))
665 sprintf (command
, "%s %s", sha256sum_path
, destination
);
667 fprintf (stderr
, "%s\n", command
);
668 fp
= popen(command
, "r");
670 die("sha256sum command did not succeed");
672 fgets(output
, sizeof(output
)-1, fp
);
674 printf("sha256sum: %s\n", g_strsplit_set(output
, " ", -1)[0]);
675 FILE *fpx
= fopen(digestfile
, "w");
678 fputs(g_strsplit_set(output
, " ", -1)[0], fpx
);
681 int exitstatus
= pclose(fp
);
682 if(WEXITSTATUS(exitstatus
) != 0)
683 die("sha256sum command did not succeed");
684 if (g_file_test (ascfile
, G_FILE_TEST_IS_REGULAR
))
686 sprintf (command
, "%s --detach-sign --armor %s", gpg2_path
, digestfile
);
688 fprintf (stderr
, "%s\n", command
);
689 fp
= popen(command
, "r");
690 exitstatus
= pclose(fp
);
691 if(WEXITSTATUS(exitstatus
) != 0) {
692 fprintf (stderr
, "ERROR: gpg2 command did not succeed, could not sign, continuing\n");
694 unsigned long sig_offset
= 0;
695 unsigned long sig_length
= 0;
696 get_elf_section_offset_and_lenghth(destination
, ".sha256_sig", &sig_offset
, &sig_length
);
698 printf("sig_offset: %lu\n", sig_offset
);
699 printf("sig_length: %lu\n", sig_length
);
701 if(sig_offset
== 0) {
702 die("Could not determine offset for signature");
704 FILE *fpdst3
= fopen(destination
, "r+");
706 die("Not able to open the destination file for writing, aborting");
707 // if(strlen(updateinformation)>sig_length)
708 // die("signature does not fit into segment, aborting");
709 fseek(fpdst3
, sig_offset
, SEEK_SET
);
710 FILE *fpsrc2
= fopen(ascfile
, "rb");
711 if (fpsrc2
== NULL
) {
712 die("Not able to open the asc file for reading, aborting");
715 while (!feof(fpsrc2
))
717 fread(&byte
, sizeof(char), 1, fpsrc2
);
718 fwrite(&byte
, sizeof(char), 1, fpdst3
);
723 if (g_file_test (ascfile
, G_FILE_TEST_IS_REGULAR
))
725 if (g_file_test (digestfile
, G_FILE_TEST_IS_REGULAR
))
731 /* If updateinformation was provided, then we also generate the zsync file (after having signed the AppImage) */
732 if(updateinformation
!= NULL
){
733 gchar
*zsyncmake_path
= g_find_program_in_path ("zsyncmake");
735 fprintf (stderr
, "zsyncmake is not installed, skipping\n");
737 fprintf (stderr
, "zsyncmake is installed and updateinformation is provided, "
738 "hence generating zsync file\n");
739 sprintf (command
, "%s %s -u %s", zsyncmake_path
, destination
, basename(destination
));
741 fprintf (stderr
, "%s\n", command
);
742 fp
= popen(command
, "r");
744 die("Failed to run zsyncmake command");
745 int exitstatus
= pclose(fp
);
746 if (WEXITSTATUS(exitstatus
) != 0)
747 die("zsyncmake command did not succeed");
751 fprintf (stderr
, "Success\n");
754 /* If the first argument is a regular file, then we assume that we should unpack it */
755 if (g_file_test (remaining_args
[0], G_FILE_TEST_IS_REGULAR
)){
756 fprintf (stdout
, "%s is a file, assuming it is an AppImage and should be unpacked\n", remaining_args
[0]);
757 die("To be implemented");