feature/usage
[appimagekit/gsi.git] / appimagetool.c
blobc13939d8b7f055a43b1352cd70ee917bc6e1b280
1 /**************************************************************************
2 *
3 * Copyright (c) 2004-17 Simon Peter
4 *
5 * All Rights Reserved.
6 *
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
23 * THE SOFTWARE.
25 **************************************************************************/
27 #ident "AppImage by Simon Peter, http://appimage.org/"
29 #include <glib.h>
30 #include <glib/gstdio.h>
31 #include <stdlib.h>
33 #include <stdio.h>
34 #include <argp.h>
36 #include <stdlib.h>
37 #include <fcntl.h>
38 #include "squashfuse.h"
40 #include <sys/types.h>
41 #include <sys/stat.h>
42 #include <sys/wait.h>
44 #include "binreloc.h"
46 #include <libgen.h>
48 #include <unistd.h>
49 #include <string.h>
51 #include "elf.h"
52 #include "getsection.h"
54 extern int _binary_runtime_start;
55 extern int _binary_runtime_end;
58 static gboolean list = FALSE;
59 static gboolean verbose = FALSE;
60 static gboolean version = FALSE;
61 static gboolean sign = FALSE;
62 static gboolean no_appstream = FALSE;
63 gchar **remaining_args = NULL;
64 gchar *updateinformation = NULL;
65 gchar *bintray_user = NULL;
66 gchar *bintray_repo = NULL;
67 gchar *sqfs_comp = "gzip";
69 // #####################################################################
71 static void die(const char *msg) {
72 fprintf(stderr, "%s\n", msg);
73 exit(1);
76 /* Function that prints the contents of a squashfs file
77 * using libsquashfuse (#include "squashfuse.h") */
78 int sfs_ls(char* image) {
79 sqfs_err err = SQFS_OK;
80 sqfs_traverse trv;
81 sqfs fs;
83 unsigned long fs_offset = get_elf_size(image);
85 if ((err = sqfs_open_image(&fs, image, fs_offset)))
86 die("sqfs_open_image error");
88 if ((err = sqfs_traverse_open(&trv, &fs, sqfs_inode_root(&fs))))
89 die("sqfs_traverse_open error");
90 while (sqfs_traverse_next(&trv, &err)) {
91 if (!trv.dir_end) {
92 printf("%s\n", trv.path);
95 if (err)
96 die("sqfs_traverse_next error");
97 sqfs_traverse_close(&trv);
99 sqfs_fd_close(fs.fd);
100 return 0;
103 /* Generate a squashfs filesystem using mksquashfs on the $PATH
104 * execlp(), execvp(), and execvpe() search on the $PATH */
105 int sfs_mksquashfs(char *source, char *destination, int offset) {
106 pid_t pid = fork();
108 if (pid == -1) {
109 // error, failed to fork()
110 return(-1);
111 } else if (pid > 0) {
112 int status;
113 waitpid(pid, &status, 0);
114 } else {
115 // we are the child
116 gchar *offset_string;
117 offset_string = g_strdup_printf("%i", offset);
118 if(0==strcmp("xz", sqfs_comp))
120 // https://jonathancarter.org/2015/04/06/squashfs-performance-testing/ says:
121 // improved performance by using a 16384 block size with a sacrifice of around 3% more squashfs image space
122 execlp("mksquashfs", "mksquashfs", source, destination, "-offset", offset_string, "-comp", "xz", "-root-owned", "-noappend", "-Xdict-size", "100%", "-b", "16384", "-no-xattrs", "-root-owned", NULL);
123 } else {
124 execlp("mksquashfs", "mksquashfs", source, destination, "-offset", offset_string, "-comp", sqfs_comp, "-root-owned", "-noappend", "-no-xattrs", "-root-owned", NULL);
126 perror("execlp"); // execlp() returns only on error
127 return(-1); // exec never returns
129 return(0);
132 /* Generate a squashfs filesystem
133 * The following would work if we link to mksquashfs.o after we renamed
134 * main() to mksquashfs_main() in mksquashfs.c but we don't want to actually do
135 * this because squashfs-tools is not under a permissive license
136 * i *nt sfs_mksquashfs(char *source, char *destination) {
137 * char *child_argv[5];
138 * child_argv[0] = NULL;
139 * child_argv[1] = source;
140 * child_argv[2] = destination;
141 * child_argv[3] = "-root-owned";
142 * child_argv[4] = "-noappend";
143 * mksquashfs_main(5, child_argv);
147 gchar* find_first_matching_file(const gchar *real_path, const gchar *pattern) {
148 GDir *dir;
149 gchar *full_name;
150 gchar *resulting;
151 dir = g_dir_open(real_path, 0, NULL);
152 if (dir != NULL) {
153 const gchar *entry;
154 while ((entry = g_dir_read_name(dir)) != NULL) {
155 full_name = g_build_filename(real_path, entry, NULL);
156 if (! g_file_test(full_name, G_FILE_TEST_IS_DIR)) {
157 if(g_pattern_match_simple(pattern, entry))
158 return(full_name);
160 else {
161 resulting = find_first_matching_file(full_name, pattern);
162 if(resulting)
163 return(resulting);
166 g_dir_close(dir);
168 else {
169 g_warning("%s: %s", real_path, g_strerror(errno));
171 return NULL;
174 gchar* find_first_matching_file_nonrecursive(const gchar *real_path, const gchar *pattern) {
175 GDir *dir;
176 gchar *full_name;
177 dir = g_dir_open(real_path, 0, NULL);
178 if (dir != NULL) {
179 const gchar *entry;
180 while ((entry = g_dir_read_name(dir)) != NULL) {
181 full_name = g_build_filename(real_path, entry, NULL);
182 if (g_file_test(full_name, G_FILE_TEST_IS_REGULAR)) {
183 if(g_pattern_match_simple(pattern, entry))
184 return(full_name);
187 g_dir_close(dir);
189 else {
190 g_warning("%s: %s", real_path, g_strerror(errno));
192 return NULL;
195 gchar* get_desktop_entry(GKeyFile *kf, char *key) {
196 gchar *value = g_key_file_get_string (kf, "Desktop Entry", key, NULL);
197 if (! value){
198 fprintf(stderr, "%s entry not found in desktop file\n", key);
200 return value;
203 /* in-place modification of the string, and assuming the buffer pointed to by
204 * line is large enough to hold the resulting string*/
205 static void replacestr(char *line, const char *search, const char *replace)
207 char *sp = NULL;
209 if ((sp = strstr(line, search)) == NULL) {
210 return;
212 int search_len = strlen(search);
213 int replace_len = strlen(replace);
214 int tail_len = strlen(sp+search_len);
216 memmove(sp+replace_len,sp+search_len,tail_len+1);
217 memcpy(sp, replace, replace_len);
219 /* Do it recursively again until no more work to do */
221 if ((sp = strstr(line, search))) {
222 replacestr(line, search, replace);
226 // #####################################################################
228 static GOptionEntry entries[] =
230 { "list", 'l', 0, G_OPTION_ARG_NONE, &list, "List files in SOURCE AppImage", NULL },
231 { "updateinformation", 'u', 0, G_OPTION_ARG_STRING, &updateinformation, "Embed update information STRING; if zsyncmake is installed, generate zsync file", NULL },
232 { "bintray-user", 0, 0, G_OPTION_ARG_STRING, &bintray_user, "Bintray user name", NULL },
233 { "bintray-repo", 0, 0, G_OPTION_ARG_STRING, &bintray_repo, "Bintray repository", NULL },
234 { "version", 0, 0, G_OPTION_ARG_NONE, &version, "Show version number", NULL },
235 { "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose, "Produce verbose output", NULL },
236 { "sign", 's', 0, G_OPTION_ARG_NONE, &sign, "Sign with gpg2", NULL },
237 { "comp", 0, 0, G_OPTION_ARG_STRING, &sqfs_comp, "Squashfs compression", NULL },
238 { "no-appstream", 'n', 0, G_OPTION_ARG_NONE, &no_appstream, "Do not check AppStream metadata", NULL },
239 { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &remaining_args, NULL, NULL },
240 { 0,0,0,0,0,0,0 }
244 main (int argc, char *argv[])
246 /* Parse VERSION environment variable.
247 * We cannot use g_environ_getenv (g_get_environ() since it is too new for CentOS 6 */
248 char* version_env;
249 version_env = getenv("VERSION");
251 /* Parse OWD environment variable.
252 * If it is available then cd there. It is the original CWD prior to running AppRun */
253 char* owd_env = NULL;
254 owd_env = getenv("OWD");
255 if(NULL!=owd_env){
256 int ret;
257 ret = chdir(owd_env);
258 if (ret != 0){
259 fprintf(stderr, "Could not cd into %s\n", owd_env);
260 exit(1);
265 GError *error = NULL;
266 GOptionContext *context;
267 char command[PATH_MAX];
269 context = g_option_context_new ("SOURCE [DESTINATION] - Generate, extract, and inspect AppImages");
270 g_option_context_add_main_entries (context, entries, NULL);
271 // g_option_context_add_group (context, gtk_get_option_group (TRUE));
272 if (!g_option_context_parse (context, &argc, &argv, &error))
274 fprintf(stderr, "Option parsing failed: %s\n", error->message);
275 exit(1);
278 if(version){
279 fprintf(stderr,"Version: %s\n", VERSION_NUMBER);
280 exit(0);
283 if(!((0 == strcmp(sqfs_comp, "gzip")) || (0 ==strcmp(sqfs_comp, "xz"))))
284 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.");
285 /* Check for dependencies here. Better fail early if they are not present. */
286 if(! g_find_program_in_path ("mksquashfs"))
287 die("mksquashfs is missing but required, please install it");
288 if(! g_find_program_in_path ("zsyncmake"))
289 g_print("WARNING: zsyncmake is missing, please install it if you want to use binary delta updates\n");
290 if(! no_appstream)
291 if(! g_find_program_in_path ("appstreamcli"))
292 g_print("WARNING: appstreamcli is missing, please install it if you want to use AppStream metadata\n");
293 if(! g_find_program_in_path ("gpg2"))
294 g_print("WARNING: gpg2 is missing, please install it if you want to create digital signatures\n");
295 if(! g_find_program_in_path ("sha256sum"))
296 g_print("WARNING: sha256sum is missing, please install it if you want to create digital signatures\n");
298 if(!&remaining_args[0])
299 die("SOURCE is missing");
301 /* If in list mode */
302 if (list){
303 sfs_ls(remaining_args[0]);
304 exit(0);
307 /* If the first argument is a directory, then we assume that we should package it */
308 if (g_file_test (remaining_args[0], G_FILE_TEST_IS_DIR)){
309 char *destination;
310 char source[PATH_MAX];
311 realpath(remaining_args[0], source);
313 /* Check if *.desktop file is present in source AppDir */
314 gchar *desktop_file = find_first_matching_file_nonrecursive(source, "*.desktop");
315 if(desktop_file == NULL){
316 die("$ID.desktop file not found");
318 if(verbose)
319 fprintf (stdout, "Desktop file: %s\n", desktop_file);
321 /* Read information from .desktop file */
322 GKeyFile *kf = g_key_file_new ();
323 if (!g_key_file_load_from_file (kf, desktop_file, 0, NULL))
324 die(".desktop file cannot be parsed");
326 if(verbose){
327 fprintf (stderr,"Name: %s\n", get_desktop_entry(kf, "Name"));
328 fprintf (stderr,"Icon: %s\n", get_desktop_entry(kf, "Icon"));
329 fprintf (stderr,"Exec: %s\n", get_desktop_entry(kf, "Exec"));
330 fprintf (stderr,"Comment: %s\n", get_desktop_entry(kf, "Comment"));
331 fprintf (stderr,"Type: %s\n", get_desktop_entry(kf, "Type"));
332 fprintf (stderr,"Categories: %s\n", get_desktop_entry(kf, "Categories"));
335 /* Determine the architecture */
336 gchar *arch = getenv("ARCH");
337 FILE *fp;
338 /* If no $ARCH variable is set check a file */
339 if (!arch) {
340 gchar *archfile = NULL;
341 /* We use the next best .so that we can find to determine the architecture */
342 archfile = find_first_matching_file(source, "*.so.*");
343 if(!archfile)
345 /* If we found no .so we try to guess the main executable - this might be a script though */
346 // char guessed_bin_path[PATH_MAX];
347 // sprintf (guessed_bin_path, "%s/usr/bin/%s", source, g_strsplit_set(get_desktop_entry(kf, "Exec"), " ", -1)[0]);
348 // archfile = guessed_bin_path;
349 archfile = "/proc/self/exe";
351 if(verbose)
352 fprintf (stderr,"File used for determining architecture: %s\n", archfile);
354 char line[PATH_MAX];
355 char command[PATH_MAX];
356 sprintf (command, "/usr/bin/file -L -N -b %s", archfile);
357 fp = popen(command, "r");
358 if (fp == NULL)
359 die("Failed to run file command");
360 fgets(line, sizeof(line)-1, fp);
361 arch = g_strstrip(g_strsplit_set(line, ",", -1)[1]);
362 replacestr(arch, "-", "_");
363 fprintf (stderr,"Arch: %s\n", arch+1);
364 pclose(fp);
366 if(!arch)
368 printf("The architecture could not be determined, assuming 'all'\n");
369 arch="all";
373 char app_name_for_filename[PATH_MAX];
374 sprintf(app_name_for_filename, "%s", get_desktop_entry(kf, "Name"));
375 replacestr(app_name_for_filename, " ", "_");
377 if(verbose)
378 fprintf (stderr,"App name for filename: %s\n", app_name_for_filename);
380 if (remaining_args[1]) {
381 destination = remaining_args[1];
382 } else {
383 /* No destination has been specified, to let's construct one
384 * TODO: Find out the architecture and use a $VERSION that might be around in the env */
385 char dest_path[PATH_MAX];
386 sprintf (dest_path, "%s-%s.AppImage", app_name_for_filename, arch);
388 if(verbose)
389 fprintf (stderr,"dest_path: %s\n", dest_path);
391 if (version_env!=NULL)
392 sprintf (dest_path, "%s-%s-%s.AppImage", app_name_for_filename, version_env, arch);
394 destination = dest_path;
395 replacestr(destination, " ", "_");
397 // destination = basename(br_strcat(source, ".AppImage"));
398 fprintf (stdout, "DESTINATION not specified, so assuming %s\n", destination);
400 fprintf (stdout, "%s should be packaged as %s\n", source, destination);
401 /* Check if the Icon file is how it is expected */
402 gchar* icon_name = get_desktop_entry(kf, "Icon");
403 gchar* icon_file_path = NULL;
404 gchar* icon_file_png;
405 gchar* icon_file_svg;
406 gchar* icon_file_svgz;
407 gchar* icon_file_xpm;
408 icon_file_png = g_strdup_printf("%s/%s.png", source, icon_name);
409 icon_file_svg = g_strdup_printf("%s/%s.svg", source, icon_name);
410 icon_file_svgz = g_strdup_printf("%s/%s.svgz", source, icon_name);
411 icon_file_xpm = g_strdup_printf("%s/%s.xpm", source, icon_name);
412 if (g_file_test(icon_file_png, G_FILE_TEST_IS_REGULAR)) {
413 icon_file_path = icon_file_png;
414 } else if(g_file_test(icon_file_svg, G_FILE_TEST_IS_REGULAR)) {
415 icon_file_path = icon_file_svg;
416 } else if(g_file_test(icon_file_svgz, G_FILE_TEST_IS_REGULAR)) {
417 icon_file_path = icon_file_svgz;
418 } else if(g_file_test(icon_file_xpm, G_FILE_TEST_IS_REGULAR)) {
419 icon_file_path = icon_file_xpm;
420 } else {
421 fprintf (stderr, "%s{.png,.svg,.svgz,.xpm} not present but defined in desktop file\n", icon_name);
422 exit(1);
425 /* Check if .DirIcon is present in source AppDir */
426 gchar *diricon_path = g_build_filename(source, ".DirIcon", NULL);
428 if (! g_file_test(diricon_path, G_FILE_TEST_EXISTS)){
429 fprintf (stderr, "Deleting pre-existing .DirIcon\n");
430 g_unlink(diricon_path);
432 if (! g_file_test(diricon_path, G_FILE_TEST_IS_REGULAR)){
433 fprintf (stderr, "Creating .DirIcon symlink based on information from desktop file\n");
434 int res = symlink(basename(icon_file_path), diricon_path);
435 if(res)
436 die("Could not symlink .DirIcon");
439 /* Check if AppStream upstream metadata is present in source AppDir */
440 if(! no_appstream){
441 char application_id[PATH_MAX];
442 sprintf (application_id, "%s", basename(desktop_file));
443 replacestr(application_id, ".desktop", ".appdata.xml");
444 gchar *appdata_path = g_build_filename(source, "/usr/share/metainfo/", application_id, NULL);
445 if (! g_file_test(appdata_path, G_FILE_TEST_IS_REGULAR)){
446 fprintf (stderr, "WARNING: AppStream upstream metadata is missing, please consider creating it\n");
447 fprintf (stderr, " in usr/share/metainfo/%s\n", application_id);
448 fprintf (stderr, " Please see https://www.freedesktop.org/software/appstream/docs/chap-Quickstart.html#sect-Quickstart-DesktopApps\n");
449 fprintf (stderr, " for more information.\n");
450 /* As a courtesy, generate one to be filled by the user */
451 if(g_find_program_in_path ("appstream-util")) {
452 gchar *appdata_dir = g_build_filename(source, "/usr/share/metainfo/", NULL);
453 g_mkdir_with_parents(appdata_dir, 0755);
454 sprintf (command, "%s appdata-from-desktop %s %s", g_find_program_in_path ("appstream-util"), desktop_file, appdata_path);
455 int ret = system(command);
456 if (ret != 0)
457 die("Failed to generate AppStream template");
458 fprintf (stderr, "AppStream template has been generated in in %s, please edit it\n", appdata_path);
459 exit(1);
461 } else {
462 fprintf (stderr, "AppStream upstream metadata found in usr/share/metainfo/%s\n", application_id);
463 /* Use ximion's appstreamcli to make sure that desktop file and appdata match together */
464 if(g_find_program_in_path ("appstreamcli")) {
465 sprintf (command, "%s validate-tree %s", g_find_program_in_path ("appstreamcli"), source);
466 int ret = system(command);
467 if (ret != 0)
468 die("Failed to validate AppStream information with appstreamcli");
470 /* It seems that hughsie's appstream-util does additional validations */
471 if(g_find_program_in_path ("appstream-util")) {
472 sprintf (command, "%s validate-relax %s", g_find_program_in_path ("appstream-util"), appdata_path);
473 int ret = system(command);
474 if (ret != 0)
475 die("Failed to validate AppStream information with appstream-util");
480 /* Upstream mksquashfs can currently not start writing at an offset,
481 * so we need a patched one. https://github.com/plougher/squashfs-tools/pull/13
482 * should hopefully change that. */
484 fprintf (stderr, "Generating squashfs...\n");
485 /* runtime is embedded into this executable
486 * http://stupefydeveloper.blogspot.de/2008/08/cc-embed-binary-data-into-elf.html */
487 int size = (int)((void *)&_binary_runtime_end - (void *)&_binary_runtime_start);
488 char *data = (char *)&_binary_runtime_start;
489 if (verbose)
490 printf("Size of the embedded runtime: %d bytes\n", size);
492 int result = sfs_mksquashfs(source, destination, size);
493 if(result != 0)
494 die("sfs_mksquashfs error");
496 fprintf (stderr, "Embedding ELF...\n");
497 FILE *fpdst = fopen(destination, "rb+");
498 if (fpdst == NULL) {
499 die("Not able to open the AppImage for writing, aborting");
502 fseek(fpdst, 0, SEEK_SET);
503 fwrite(data, size, 1, fpdst);
504 fclose(fpdst);
506 fprintf (stderr, "Marking the AppImage as executable...\n");
507 if (chmod (destination, 0755) < 0) {
508 printf("Could not set executable bit, aborting\n");
509 exit(1);
512 if(bintray_user != NULL){
513 if(bintray_repo != NULL){
514 char buf[1024];
515 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);
516 updateinformation = buf;
517 printf("%s\n", updateinformation);
521 /* If updateinformation was provided, then we check and embed it */
522 if(updateinformation != NULL){
523 if(!g_str_has_prefix(updateinformation,"zsync|"))
524 if(!g_str_has_prefix(updateinformation,"bintray-zsync|"))
525 die("The provided updateinformation is not in a recognized format");
527 gchar **ui_type = g_strsplit_set(updateinformation, "|", -1);
529 if(verbose)
530 printf("updateinformation type: %s\n", ui_type[0]);
531 /* TODO: Further checking of the updateinformation */
534 unsigned long ui_offset = 0;
535 unsigned long ui_length = 0;
536 get_elf_section_offset_and_lenghth(destination, ".upd_info", &ui_offset, &ui_length);
537 if(verbose) {
538 printf("ui_offset: %lu\n", ui_offset);
539 printf("ui_length: %lu\n", ui_length);
541 if(ui_offset == 0) {
542 die("Could not determine offset for updateinformation");
543 } else {
544 if(strlen(updateinformation)>ui_length)
545 die("updateinformation does not fit into segment, aborting");
546 FILE *fpdst2 = fopen(destination, "r+");
547 if (fpdst2 == NULL)
548 die("Not able to open the destination file for writing, aborting");
549 fseek(fpdst2, ui_offset, SEEK_SET);
550 // fseek(fpdst2, ui_offset, SEEK_SET);
551 // fwrite(0x00, 1, 1024, fpdst); // FIXME: Segfaults; why?
552 // fseek(fpdst, ui_offset, SEEK_SET);
553 fwrite(updateinformation, strlen(updateinformation), 1, fpdst2);
554 fclose(fpdst2);
558 if(sign){
559 /* The user has indicated that he wants to sign */
560 gchar *gpg2_path = g_find_program_in_path ("gpg2");
561 gchar *sha256sum_path = g_find_program_in_path ("sha256sum");
562 if(!gpg2_path){
563 fprintf (stderr, "gpg2 is not installed, cannot sign\n");
565 else if(!sha256sum_path){
566 fprintf (stderr, "sha256sum is not installed, cannot sign\n");
567 } else {
568 fprintf (stderr, "gpg2 and sha256sum are installed and user requested to sign, "
569 "hence signing\n");
570 char *digestfile;
571 digestfile = br_strcat(destination, ".digest");
572 char *ascfile;
573 ascfile = br_strcat(destination, ".digest.asc");
574 if (g_file_test (digestfile, G_FILE_TEST_IS_REGULAR))
575 unlink(digestfile);
576 sprintf (command, "%s %s", sha256sum_path, destination);
577 if(verbose)
578 fprintf (stderr, "%s\n", command);
579 fp = popen(command, "r");
580 if (fp == NULL)
581 die("sha256sum command did not succeed");
582 char output[1024];
583 fgets(output, sizeof(output)-1, fp);
584 if(verbose)
585 printf("sha256sum: %s\n", g_strsplit_set(output, " ", -1)[0]);
586 FILE *fpx = fopen(digestfile, "w");
587 if (fpx != NULL)
589 fputs(g_strsplit_set(output, " ", -1)[0], fpx);
590 fclose(fpx);
592 if(WEXITSTATUS(pclose(fp)) != 0)
593 die("sha256sum command did not succeed");
594 if (g_file_test (ascfile, G_FILE_TEST_IS_REGULAR))
595 unlink(ascfile);
596 sprintf (command, "%s --detach-sign --armor %s", gpg2_path, digestfile);
597 if(verbose)
598 fprintf (stderr, "%s\n", command);
599 fp = popen(command, "r");
600 if(WEXITSTATUS(pclose(fp)) != 0) {
601 fprintf (stderr, "ERROR: gpg2 command did not succeed, could not sign, continuing\n");
602 } else {
603 unsigned long sig_offset = 0;
604 unsigned long sig_length = 0;
605 get_elf_section_offset_and_lenghth(destination, ".sha256_sig", &sig_offset, &sig_length);
606 if(verbose) {
607 printf("sig_offset: %lu\n", sig_offset);
608 printf("sig_length: %lu\n", sig_length);
610 if(sig_offset == 0) {
611 die("Could not determine offset for signature");
612 } else {
613 FILE *fpdst3 = fopen(destination, "r+");
614 if (fpdst3 == NULL)
615 die("Not able to open the destination file for writing, aborting");
616 // if(strlen(updateinformation)>sig_length)
617 // die("signature does not fit into segment, aborting");
618 fseek(fpdst3, sig_offset, SEEK_SET);
619 FILE *fpsrc2 = fopen(ascfile, "rb");
620 if (fpsrc2 == NULL) {
621 die("Not able to open the asc file for reading, aborting");
623 char byte;
624 while (!feof(fpsrc2))
626 fread(&byte, sizeof(char), 1, fpsrc2);
627 fwrite(&byte, sizeof(char), 1, fpdst3);
629 fclose(fpsrc2);
630 fclose(fpdst3);
632 if (g_file_test (ascfile, G_FILE_TEST_IS_REGULAR))
633 unlink(ascfile);
634 if (g_file_test (digestfile, G_FILE_TEST_IS_REGULAR))
635 unlink(digestfile);
640 /* If updateinformation was provided, then we also generate the zsync file (after having signed the AppImage) */
641 if(updateinformation != NULL){
642 gchar *zsyncmake_path = g_find_program_in_path ("zsyncmake");
643 if(!zsyncmake_path){
644 fprintf (stderr, "zsyncmake is not installed, skipping\n");
645 } else {
646 fprintf (stderr, "zsyncmake is installed and updateinformation is provided, "
647 "hence generating zsync file\n");
648 sprintf (command, "%s %s -u %s", zsyncmake_path, destination, basename(destination));
649 if(verbose)
650 fprintf (stderr, "%s\n", command);
651 fp = popen(command, "r");
652 if (fp == NULL)
653 die("Failed to run zsyncmake command");
657 fprintf (stderr, "Success\n");
660 /* If the first argument is a regular file, then we assume that we should unpack it */
661 if (g_file_test (remaining_args[0], G_FILE_TEST_IS_REGULAR)){
662 fprintf (stdout, "%s is a file, assuming it is an AppImage and should be unpacked\n", remaining_args[0]);
663 die("To be implemented");
666 return 0;