summaryrefslogtreecommitdiff
path: root/src/install-pkglist.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/install-pkglist.c')
-rw-r--r--src/install-pkglist.c2033
1 files changed, 2033 insertions, 0 deletions
diff --git a/src/install-pkglist.c b/src/install-pkglist.c
new file mode 100644
index 0000000..7e0cc00
--- /dev/null
+++ b/src/install-pkglist.c
@@ -0,0 +1,2033 @@
+
+/**********************************************************************
+
+ Copyright 2019 Andrey V.Kosteltsev
+
+ Licensed under the Radix.pro License, Version 1.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ https://radix.pro/licenses/LICENSE-1.0-en_US.txt
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied.
+
+ **********************************************************************/
+
+#include <config.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/sysinfo.h>
+#include <sys/types.h>
+#include <stdint.h>
+#include <dirent.h>
+#include <sys/stat.h> /* chmod(2) */
+#include <sys/file.h> /* flock(2) */
+#include <fcntl.h>
+#include <linux/limits.h>
+#include <alloca.h> /* alloca(3) */
+#include <string.h> /* strdup(3) */
+#include <strings.h> /* index(3) */
+#include <libgen.h> /* basename(3) */
+#include <ctype.h> /* tolower(3) */
+#include <errno.h>
+#include <time.h>
+#include <sys/time.h>
+#include <pwd.h>
+#include <grp.h>
+#include <stdarg.h>
+#include <unistd.h>
+
+#include <pthread.h>
+
+#include <math.h>
+
+#include <sys/wait.h>
+
+#include <sys/resource.h>
+
+#include <signal.h>
+#if !defined SIGCHLD && defined SIGCLD
+# define SIGCHLD SIGCLD
+#endif
+
+#define _GNU_SOURCE
+#include <getopt.h>
+
+#include <msglog.h>
+#include <wrapper.h>
+#include <system.h>
+#include <dlist.h>
+
+#if defined( HAVE_DIALOG )
+#include <dialog-ui.h>
+#endif
+
+#define PROGRAM_NAME "install-pkglist"
+
+#include <defs.h>
+
+#define WAIT_USEC_FOR_CHILD 10000
+
+char *program = PROGRAM_NAME;
+char *root = NULL, *srcdir = NULL, *pkglist_fname = NULL,
+ *tmpdir = NULL, *curdir = NULL;
+
+int rqck = 0, gpgck = 0, progress = 0, parallel = 0, error_pkgs_list = 0, ncpus = 0;
+
+int exit_status = EXIT_SUCCESS; /* errors counter */
+char *selfdir = NULL;
+
+int __done = 0, __child = 0, __terminated = 0, __successful = 0, __all = 0;
+
+enum _install_mode {
+ CONSOLE = 0,
+ INFODIALOG,
+ MENUDIALOG
+} install_mode = CONSOLE;
+
+enum _input_type {
+ IFMT_PKG = 0,
+ IFMT_LOG,
+
+ IFMT_UNKNOWN
+};
+
+/*********************************************
+ Package structures and declarations:
+ */
+enum _procedure
+{
+ INSTALL = 0, /* 'install' */
+ UPDATE /* 'update' */
+};
+
+enum _priority
+{
+ REQUIRED = 0, /* synonims: REQUIRED | required | REQ | req */
+ RECOMMENDED, /* synonims: RECOMMENDED | recommended | REC | rec */
+ OPTIONAL, /* synonims: OPTIONAL | optional | OPT | opt */
+ SKIP /* synonims: SKIP | skip | SKP | skp */
+};
+
+struct pkg
+{
+ char *group;
+ char *name;
+ char *version;
+};
+
+struct package
+{
+ char *name;
+ char *version;
+ char *group;
+ char *description;
+
+ char *tarball;
+
+ enum _procedure procedure; /* install procedure */
+ enum _priority priority; /* install user priority */
+};
+
+enum _priority install_priority = OPTIONAL; /* by default allow all packages exept 'SKIP' */
+
+struct dlist *requires = NULL; /* list of pkg structs */
+struct dlist *packages = NULL; /* list of package structs */
+
+static void free_requires( void );
+static void free_packages( void );
+
+/*
+ End of package structures and declarations.
+ *********************************************/
+
+/*********************************************
+ Return status declarations:
+ */
+struct pkgrc
+{
+ pid_t pid;
+ int status;
+ char *name;
+ char *version;
+ char *group;
+};
+
+struct dlist *pkgrcl = NULL; /* list of pkgrc structs */
+
+static void free_pkgrcl( void );
+static struct pkgrc *find_pkgrc( struct dlist *list, pid_t pid );
+/*
+ End of return status declarations.
+ *********************************************/
+
+
+void free_resources()
+{
+ if( root ) { free( root ); root = NULL; }
+ if( srcdir ) { free( srcdir ); srcdir = NULL; }
+ if( pkglist_fname ) { free( pkglist_fname ); pkglist_fname = NULL; }
+
+ if( requires ) free_requires();
+ if( packages ) free_packages();
+ if( pkgrcl ) free_pkgrcl();
+
+ if( curdir ) { free( curdir ); curdir = NULL; }
+ if( selfdir ) { free( selfdir ); selfdir = NULL; }
+}
+
+void usage()
+{
+ free_resources();
+
+ fprintf( stdout, "\n" );
+ fprintf( stdout, "Usage: %s [options] <pkglist>\n", program );
+ fprintf( stdout, "\n" );
+ fprintf( stdout, "Create Packages List in the installation order from set of PKGLOGs\n" );
+ fprintf( stdout, "or set of PACKAGEs placed in the source directory. If the source\n" );
+ fprintf( stdout, "directory is not defined then directory of <pkglist> will be used\n" );
+ fprintf( stdout, "as source PACKAGE directory.\n" );
+ fprintf( stdout, "\n" );
+ fprintf( stdout, "Options:\n" );
+ fprintf( stdout, " -h,--help Display this information.\n" );
+ fprintf( stdout, " -v,--version Display the version of %s utility.\n", program );
+
+ fprintf( stdout, " -c,--check-requires Check the list of requires before install.\n" );
+ fprintf( stdout, " -g,--gpg-verify Verify GPG2 signature. The signature must be\n" );
+ fprintf( stdout, " saved in a file whose name is the same as the\n" );
+ fprintf( stdout, " package file name, but with the extension '.asc'\n" );
+ fprintf( stdout, " and located in the same directory as the package.\n" );
+#if defined( HAVE_DIALOG )
+ fprintf( stdout, " -i,--info-dialog Show package description during install\n" );
+ fprintf( stdout, " process using ncurses dialog.\n" );
+ fprintf( stdout, " -m,--menu-dialog Ask for confirmation the inatallation.\n" );
+#endif
+ fprintf( stdout, " --parallel Parallel installation (dangerous; required the\n" );
+ fprintf( stdout, " checking of DB integrity after installation).\n" );
+ fprintf( stdout, " --errlist Print the list of not installed packages to the\n" );
+ fprintf( stdout, " stderr in following format:\n" );
+ fprintf( stdout, " group/name:version:status\n" );
+ fprintf( stdout, " This option is applicable only for parallel\n" );
+ fprintf( stdout, " installations.\n" );
+ fprintf( stdout, " --progress Show progress bar instead of packages information.\n" );
+
+ fprintf( stdout, " -p,--priority=<required|recommended|optional|all>\n" );
+ fprintf( stdout, " Рackage priorities allowed for installation:\n" );
+ fprintf( stdout, " - optional | all ) install all packages;\n" );
+ fprintf( stdout, " - recommended ) install required and recommended;\n" );
+ fprintf( stdout, " - required ) install only required packages.\n" );
+
+ fprintf( stdout, " -r,--root=<DIR> Target rootfs path.\n" );
+
+ fprintf( stdout, " -s,--source=<DIR> Packages source directory.\n" );
+
+ fprintf( stdout, "\n" );
+ fprintf( stdout, "Parameter:\n" );
+ fprintf( stdout, " <DIR|pkglist> Input PKGLIST file name or a source\n" );
+ fprintf( stdout, " directory to find default PKGLIST.\n" );
+ fprintf( stdout, "\n" );
+ fprintf( stdout, "If sourse packages directory is defined by option -s,--source then\n" );
+ fprintf( stdout, "specified <DIR|pkglist> argumet will be considered relative to the\n" );
+ fprintf( stdout, "source directory.\n" );
+ fprintf( stdout, "\n" );
+
+ exit( EXIT_FAILURE );
+}
+
+void to_lowercase( char *s )
+{
+ char *p = s;
+ while( p && *p ) { int c = *p; *p = tolower( c ); ++p; }
+}
+
+void to_uppercase( char *s )
+{
+ char *p = s;
+ while( p && *p ) { int c = *p; *p = toupper( c ); ++p; }
+}
+
+void version()
+{
+ char *upper = NULL;
+
+ upper = (char *)alloca( strlen( program ) + 1 );
+
+ strcpy( (char *)upper, (const char *)program );
+ to_uppercase( upper );
+
+ fprintf( stdout, "%s (%s) %s\n", program, upper, PROGRAM_VERSION );
+
+ fprintf( stdout, "Copyright (C) 2019 Andrey V.Kosteltsev.\n" );
+ fprintf( stdout, "This is free software. There is NO warranty; not even\n" );
+ fprintf( stdout, "for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n" );
+ fprintf( stdout, "\n" );
+
+ free_resources();
+ exit( EXIT_SUCCESS );
+}
+
+
+static void remove_trailing_slash( char *dir )
+{
+ char *s;
+
+ if( !dir || dir[0] == '\0' ) return;
+
+ s = dir + strlen( dir ) - 1;
+ while( *s == '/' )
+ {
+ *s = '\0'; --s;
+ }
+}
+
+
+static int _mkdir_p( const char *dir, const mode_t mode )
+{
+ char *buf;
+ char *p = NULL;
+ struct stat sb;
+
+ if( !dir ) return -1;
+
+ buf = (char *)alloca( strlen( dir ) + 1 );
+ strcpy( buf, dir );
+
+ remove_trailing_slash( buf );
+
+ /* check if path exists and is a directory */
+ if( stat( buf, &sb ) == 0 )
+ {
+ if( S_ISDIR(sb.st_mode) )
+ {
+ return 0;
+ }
+ }
+
+ /* mkdir -p */
+ for( p = buf + 1; *p; ++p )
+ {
+ if( *p == '/' )
+ {
+ *p = 0;
+ /* test path */
+ if( stat( buf, &sb ) != 0 )
+ {
+ /* path does not exist - create directory */
+ if( mkdir( buf, mode ) < 0 )
+ {
+ return -1;
+ }
+ } else if( !S_ISDIR(sb.st_mode) )
+ {
+ /* not a directory */
+ return -1;
+ }
+ *p = '/';
+ }
+ }
+
+ /* test path */
+ if( stat( buf, &sb ) != 0 )
+ {
+ /* path does not exist - create directory */
+ if( mkdir( buf, mode ) < 0 )
+ {
+ return -1;
+ }
+ } else if( !S_ISDIR(sb.st_mode) )
+ {
+ /* not a directory */
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static void _rm_tmpdir( const char *dirpath )
+{
+ DIR *dir;
+ char *path;
+ size_t len;
+
+ struct stat path_sb, entry_sb;
+ struct dirent *entry;
+
+ if( stat( dirpath, &path_sb ) == -1 )
+ {
+ return; /* stat returns error code; errno is set */
+ }
+
+ if( S_ISDIR(path_sb.st_mode) == 0 )
+ {
+ return; /* dirpath is not a directory */
+ }
+
+ if( (dir = opendir(dirpath) ) == NULL )
+ {
+ return; /* Cannot open direcroty; errno is set */
+ }
+
+ len = strlen( dirpath );
+
+ while( (entry = readdir( dir )) != NULL)
+ {
+
+ /* skip entries '.' and '..' */
+ if( ! strcmp( entry->d_name, "." ) || ! strcmp( entry->d_name, ".." ) ) continue;
+
+ /* determinate a full name of an entry */
+ path = alloca( len + strlen( entry->d_name ) + 2 );
+ strcpy( path, dirpath );
+ strcat( path, "/" );
+ strcat( path, entry->d_name );
+
+ if( stat( path, &entry_sb ) == 0 )
+ {
+ if( S_ISDIR(entry_sb.st_mode) )
+ {
+ /* recursively remove a nested directory */
+ _rm_tmpdir( path );
+ }
+ else
+ {
+ /* remove a file object */
+ (void)unlink( path );
+ }
+ }
+ /* else { stat() returns error code; errno is set; and we have to continue the loop } */
+
+ }
+
+ /* remove the devastated directory and close the object of this directory */
+ (void)rmdir( dirpath );
+
+ closedir( dir );
+}
+
+
+static char *_mk_tmpdir( void )
+{
+ char *buf = NULL, *p, *tmp = "/tmp";
+ size_t len = 0, size = 0;
+
+ (void)umask( S_IWGRP | S_IWOTH ); /* octal 022 */
+
+ /* Get preferred directory for tmp files */
+ if( (p = getenv( "TMP" )) != NULL ) {
+ tmp = p;
+ }
+ else if( (p = getenv( "TEMP" )) != NULL ) {
+ tmp = p;
+ }
+
+ size = strlen( tmp ) + strlen( DISTRO_NAME ) + strlen( program ) + 12;
+
+ buf = (char *)malloc( size );
+ if( !buf ) return NULL;
+
+ len = snprintf( buf, size, (const char *)"%s/%s/%s-%.7u", tmp, DISTRO_NAME, program, getpid() );
+ if( len == 0 || len == size - 1 )
+ {
+ free( buf ); return NULL;
+ }
+
+ _rm_tmpdir( (const char *)&buf[0] );
+
+ if( _mkdir_p( buf, S_IRWXU | S_IRWXG | S_IRWXO ) == 0 )
+ {
+ return buf;
+ }
+
+ free( buf ); return NULL;
+}
+
+
+
+void fatal_error_actions( void )
+{
+ logmsg( errlog, MSG_NOTICE, "Free resources on FATAL error..." );
+ if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); }
+ free_resources();
+}
+
+void sigint( int signum )
+{
+ (void)signum;
+
+ if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); }
+ free_resources();
+}
+
+void sigchld( int signum )
+{
+ pid_t pid = 0;
+ int status;
+
+ (void)signum;
+
+ while( (pid = waitpid( -1, &status, WNOHANG )) > 0 )
+ {
+ struct pkgrc *pkgrc = find_pkgrc( pkgrcl, pid );
+
+ ++__terminated; /* One of children with 'pid' is terminated */
+
+ if( WIFEXITED( status ) )
+ {
+ if( (int)WEXITSTATUS( status ) > 0 )
+ {
+ ++exit_status; /* printf( "Child %d returned non zero status: %d\n", pid, (int)WEXITSTATUS (status) ); */
+ if( pkgrc ) pkgrc->status = (int)WEXITSTATUS (status);
+ }
+ else
+ {
+ ++__successful; /* printf( "Child %d terminated with status: %d\n", pid, (int)WEXITSTATUS (status) ); */
+ if( pkgrc ) pkgrc->status = (int)WEXITSTATUS (status);
+ }
+ }
+ else if( WIFSIGNALED( status ) )
+ {
+ ++exit_status; /* printf( "Child %d terminated on signal: %d\n", pid, WTERMSIG( status ) ); */
+ if( pkgrc ) pkgrc->status = 253;
+ }
+ else
+ {
+ ++exit_status; /* printf( "Child %d terminated on unknown reason\n", pid ); */
+ if( pkgrc ) pkgrc->status = 254;
+ }
+
+ }
+
+ if( pid == -1 && errno == ECHILD )
+ {
+ /* No child processes: */
+ __done = 1;
+ }
+ return;
+}
+
+
+static void set_signal_handlers()
+{
+ struct sigaction sa;
+ sigset_t set;
+
+ memset( &sa, 0, sizeof( sa ) );
+ sa.sa_handler = sigint; /* TERM, INT */
+ sa.sa_flags = SA_RESTART;
+ sigemptyset( &set );
+ sigaddset( &set, SIGTERM );
+ sigaddset( &set, SIGINT );
+ sa.sa_mask = set;
+ sigaction( SIGTERM, &sa, NULL );
+ sigaction( SIGINT, &sa, NULL );
+
+ /* System V fork+wait does not work if SIGCHLD is ignored */
+ memset( &sa, 0, sizeof( sa ) );
+ sa.sa_handler = sigchld; /* CHLD */
+ sa.sa_flags = SA_RESTART;
+ sigemptyset( &set );
+ sigaddset( &set, SIGCHLD );
+ sa.sa_mask = set;
+ sigaction( SIGCHLD, &sa, NULL );
+
+ memset( &sa, 0, sizeof( sa ) ); /* ignore SIGPIPE */
+ sa.sa_handler = SIG_IGN;
+ sa.sa_flags = 0;
+ sigaction( SIGPIPE, &sa, NULL );
+}
+
+
+static enum _input_type check_package_file( char *uncompress, const char *fname )
+{
+ struct stat st;
+ size_t pkglog_size = 0;
+ unsigned char buf[8];
+ int rc, fd;
+
+ /* SIGNATURES: https://www.garykessler.net/library/file_sigs.html */
+
+ if( uncompress )
+ {
+ *uncompress = '\0';
+ }
+
+ if( stat( fname, &st ) == -1 )
+ {
+ FATAL_ERROR( "Cannot access %s file: %s", basename( (char *)fname ), strerror( errno ) );
+ }
+
+ pkglog_size = st.st_size;
+
+ if( (fd = open( fname, O_RDONLY )) == -1 )
+ {
+ FATAL_ERROR( "Cannot open %s file: %s", basename( (char *)fname ), strerror( errno ) );
+ }
+
+ rc = (int)read( fd, (void *)&buf[0], 7 );
+ if( rc != 7 )
+ {
+ close( fd ); return IFMT_UNKNOWN;
+ }
+ buf[7] = '\0';
+
+ /* TEXT */
+ if( !strncmp( (const char *)&buf[0], "PACKAGE", 7 ) )
+ {
+ close( fd ); return IFMT_LOG;
+ }
+
+ /* GZ */
+ if( buf[0] == 0x1F && buf[1] == 0x8B && buf[2] == 0x08 )
+ {
+ if( uncompress ) { *uncompress = 'x'; }
+ close( fd ); return IFMT_PKG;
+ }
+
+ /* BZ2 */
+ if( buf[0] == 0x42 && buf[1] == 0x5A && buf[2] == 0x68 )
+ {
+ if( uncompress ) { *uncompress = 'j'; }
+ close( fd ); return IFMT_PKG;
+ }
+
+ /* XZ */
+ if( buf[0] == 0xFD && buf[1] == 0x37 && buf[2] == 0x7A &&
+ buf[3] == 0x58 && buf[4] == 0x5A && buf[5] == 0x00 )
+ {
+ if( uncompress ) { *uncompress = 'J'; }
+ close( fd ); return IFMT_PKG;
+ }
+
+ if( pkglog_size > 262 )
+ {
+ if( lseek( fd, 257, SEEK_SET ) == -1 )
+ {
+ FATAL_ERROR( "Cannot check signature of %s file: %s", basename( (char *)fname ), strerror( errno ) );
+ }
+ rc = (int)read( fd, &buf[0], 5 );
+ if( rc != 5 )
+ {
+ FATAL_ERROR( "Cannot read signature of %s file", basename( (char *)fname ) );
+ }
+ /* TAR */
+ if( buf[0] == 0x75 && buf[1] == 0x73 && buf[2] == 0x74 && buf[3] == 0x61 && buf[4] == 0x72 )
+ {
+ close( fd ); return IFMT_PKG;
+ }
+ }
+
+ close( fd ); return IFMT_UNKNOWN;
+}
+
+
+void get_args( int argc, char *argv[] )
+{
+#if defined( HAVE_DIALOG )
+ const char* short_options = "hvcgimp:r:s:";
+#else
+ const char* short_options = "hvcgp:r:s:";
+#endif
+
+#define PROGRESS 812
+#define PARALLEL 872
+#define _ERRLIST 873
+
+ const struct option long_options[] =
+ {
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, 'v' },
+ { "check-requires", no_argument, NULL, 'c' },
+ { "gpg-verify", no_argument, NULL, 'g' },
+#if defined( HAVE_DIALOG )
+ { "info-dialog", no_argument, NULL, 'i' },
+ { "menu-dialog", no_argument, NULL, 'm' },
+#endif
+ { "parallel", no_argument, NULL, PARALLEL },
+ { "errlist", no_argument, NULL, _ERRLIST },
+ { "progress", no_argument, NULL, PROGRESS },
+ { "priority", required_argument, NULL, 'p' },
+ { "root", required_argument, NULL, 'r' },
+ { "source", required_argument, NULL, 's' },
+ { NULL, 0, NULL, 0 }
+ };
+
+ int ret;
+ int option_index = 0;
+
+ while( (ret = getopt_long( argc, argv, short_options, long_options, &option_index )) != -1 )
+ {
+ switch( ret )
+ {
+ case 'h':
+ {
+ usage();
+ break;
+ }
+ case 'v':
+ {
+ version();
+ break;
+ }
+ case 'c':
+ {
+ rqck = 1;
+ break;
+ }
+ case 'g':
+ {
+ gpgck = 1;
+ break;
+ }
+
+#if defined( HAVE_DIALOG )
+ case 'i':
+ {
+ install_mode = INFODIALOG;
+ break;
+ }
+ case 'm':
+ {
+ install_mode = MENUDIALOG;
+ break;
+ }
+#endif
+
+ case PARALLEL:
+ {
+ parallel = 1;
+ break;
+ }
+ case _ERRLIST:
+ {
+ error_pkgs_list = 1;
+ break;
+ }
+ case PROGRESS:
+ {
+ progress = 1;
+ break;
+ }
+
+ case 'p':
+ {
+ if( optarg != NULL )
+ {
+ char *match = NULL;
+
+ if( strlen( (const char *)optarg ) > 2 )
+ {
+ to_lowercase( optarg );
+ if( (match = strstr( optarg, "req" )) && match == optarg ) {
+ install_priority = REQUIRED;
+ }
+ else if( (match = strstr( optarg, "rec" )) && match == optarg ) {
+ install_priority = RECOMMENDED;
+ }
+ else if( (match = strstr( optarg, "opt" )) && match == optarg ) {
+ install_priority = OPTIONAL;
+ }
+ else if( (match = strstr( optarg, "all" )) && match == optarg ) {
+ install_priority = OPTIONAL;
+ }
+ else {
+ FATAL_ERROR( "Unknown --priority '%s' value", optarg );
+ }
+ }
+ else
+ {
+ FATAL_ERROR( "Unknown --priority '%s' value", optarg );
+ }
+ }
+ else
+ /* option is present but without value */
+ usage();
+ break;
+ }
+
+ case 'r':
+ {
+ if( optarg != NULL )
+ {
+ char cwd[PATH_MAX];
+
+ bzero( (void *)cwd, PATH_MAX );
+ if( optarg[0] != '/' && curdir )
+ {
+ /* skip current directory definition './' at start of argument: */
+ if( !strncmp( optarg, "./", 2 ) && strncmp( optarg, "..", 2 ) )
+ (void)sprintf( cwd, "%s/%s", curdir, optarg + 2 );
+ else if( (strlen( optarg ) == 1) && !strncmp( optarg, ".", 1 ) )
+ (void)sprintf( cwd, "%s", curdir );
+ else
+ (void)sprintf( cwd, "%s/%s", curdir, optarg );
+ root = xstrdup( (const char *)cwd );
+ }
+ else
+ {
+ root = xstrdup( (const char *)optarg );
+ }
+ remove_trailing_slash( root );
+ }
+ else
+ /* option is present but without value */
+ usage();
+ break;
+ }
+
+ case 's':
+ {
+ if( optarg != NULL )
+ {
+ char cwd[PATH_MAX];
+
+ bzero( (void *)cwd, PATH_MAX );
+ if( optarg[0] != '/' && curdir )
+ {
+ (void)sprintf( cwd, "%s/%s", curdir, optarg );
+ srcdir = xstrdup( (const char *)cwd );
+ }
+ else
+ {
+ srcdir = xstrdup( (const char *)optarg );
+ }
+ remove_trailing_slash( srcdir );
+ }
+ else
+ /* option is present but without value */
+ usage();
+ break;
+ }
+
+ case '?': default:
+ {
+ usage();
+ break;
+ }
+ }
+ }
+
+
+ /* last command line argument is the intput PKGLIST file */
+ if( optind < argc )
+ {
+ struct stat st;
+ char *buf = NULL;
+
+ bzero( (void *)&st, sizeof( struct stat ) );
+
+ buf = (char *)malloc( (size_t)PATH_MAX );
+ if( !buf ) { FATAL_ERROR( "Cannot allocate memory" ); }
+ bzero( (void *)buf, PATH_MAX );
+
+ (void)strcpy( buf, (const char *)argv[optind++] );
+ remove_trailing_slash( (char *)&buf[0] );
+
+ if( srcdir)
+ {
+ char *tmp = xstrdup( (const char *)&buf[0] );
+
+ /* Ignore already defined srcdir if absolute path is specified: */
+ if( buf[0] != '/' )
+ {
+ if( !strncmp( tmp, "./", 2 ) && strncmp( tmp, "..", 2 ) )
+ (void)sprintf( buf, "%s/%s", srcdir, tmp + 2 );
+ else if( (strlen( tmp ) == 1) && !strncmp( tmp, ".", 1 ) )
+ (void)sprintf( buf, "%s", srcdir );
+ else
+ (void)sprintf( buf, "%s/%s", srcdir, tmp );
+ }
+ free( tmp );
+
+ free( srcdir ); srcdir = xstrdup( (const char *)buf );
+ }
+ else
+ {
+ char *tmp = xstrdup( (const char *)&buf[0] );
+
+ if( buf[0] != '/' && curdir )
+ {
+ if( !strncmp( tmp, "./", 2 ) && strncmp( tmp, "..", 2 ) )
+ (void)sprintf( buf, "%s/%s", curdir, tmp + 2 );
+ else if( (strlen( tmp ) == 1) && !strncmp( tmp, ".", 1 ) )
+ (void)sprintf( buf, "%s", curdir );
+ else
+ (void)sprintf( buf, "%s/%s", curdir, tmp );
+ }
+ free( tmp );
+
+ srcdir = xstrdup( (const char *)buf );
+ }
+
+ stat( (const char *)&buf[0], &st ); /* Do not check return status */
+
+ if( S_ISDIR(st.st_mode) )
+ {
+ /**********************************************************
+ Add default '.pkglist' file name to the source dir name:
+ */
+ (void)sprintf( buf, "%s/.pkglist", srcdir );
+ pkglist_fname = xstrdup( (const char *)buf );
+ }
+ else
+ {
+ if( S_ISREG(st.st_mode) )
+ {
+ pkglist_fname = xstrdup( (const char *)buf );
+ free( srcdir ); srcdir = xstrdup( (const char *)dirname( (char *)&buf[0] ) );
+ }
+ else
+ {
+ FATAL_ERROR( "Specified '%s' PKGLIST is not a regular file", basename( (char *)&buf[0] ) );
+ }
+ }
+
+ free( buf );
+ }
+ else
+ {
+ usage();
+ }
+
+ /*********************************************
+ root is always have the trailing slash '/':
+ */
+ {
+ struct stat st;
+ char *buf = NULL;
+ int len;
+
+ bzero( (void *)&st, sizeof( struct stat ) );
+
+ buf = (char *)malloc( (size_t)PATH_MAX );
+ if( !buf ) { FATAL_ERROR( "Cannot allocate memory" ); }
+ bzero( (void *)buf, PATH_MAX );
+
+ if( !root )
+ {
+ buf[0] = '/'; buf[1] = '\0';
+ root = xstrdup( (const char *)buf );
+ }
+ else
+ {
+ len = strlen( root );
+
+ (void)strcpy( buf, (const char *)root );
+ if( buf[ len - 1 ] != '/' )
+ {
+ buf[len] = '/'; buf[len+1] = '\0';
+ free( root ); root = xstrdup( (const char *)buf );
+ }
+ }
+
+ if( stat( (const char *)&buf[0], &st ) == -1 )
+ {
+ FATAL_ERROR( "Cannot access '%s' file or directory: %s", buf, strerror( errno ) );
+ }
+ if( !S_ISDIR(st.st_mode) )
+ {
+ FATAL_ERROR( "Defined --root '%s' is not a directory", buf );
+ }
+
+ free( buf );
+ }
+ /*
+ End of set root path routine.
+ *********************************************/
+}
+
+
+
+
+/*********************************************
+ Package functions:
+ */
+static char *strprio( enum _priority priority, int short_name )
+{
+ char *p = NULL;
+
+ switch( priority )
+ {
+ case REQUIRED:
+ p = ( short_name ) ? "REQ" : "REQUIRED";
+ break;
+ case RECOMMENDED:
+ p = ( short_name ) ? "REC" : "RECOMMENDED";
+ break;
+ case OPTIONAL:
+ p = ( short_name ) ? "OPT" : "OPTIONAL";
+ break;
+ case SKIP:
+ p = ( short_name ) ? "SKP" : "SKIP";
+ break;
+ }
+ return p;
+}
+
+static char *strproc( enum _procedure procedure )
+{
+ char *p = NULL;
+
+ switch( procedure )
+ {
+ case INSTALL:
+ p = "install";
+ break;
+ case UPDATE:
+ p = "update";
+ break;
+ }
+ return p;
+}
+
+
+static struct pkg *pkg_alloc( void )
+{
+ struct pkg *pkg = NULL;
+
+ pkg = (struct pkg *)malloc( sizeof( struct pkg ) );
+ if( !pkg ) { FATAL_ERROR( "Cannot allocate memory" ); }
+ bzero( (void *)pkg, sizeof( struct pkg ) );
+
+ return pkg;
+}
+
+static void pkg_free( struct pkg *pkg )
+{
+ if( pkg )
+ {
+ if( pkg->name ) { free( pkg->name ); pkg->name = NULL; }
+ if( pkg->version ) { free( pkg->version ); pkg->version = NULL; }
+ if( pkg->group ) { free( pkg->group ); pkg->group = NULL; }
+
+ free( pkg );
+ }
+}
+
+static void __pkg_free_func( void *data, void *user_data )
+{
+ struct pkg *pkg = (struct pkg *)data;
+ if( pkg ) { pkg_free( pkg ); }
+}
+
+static void free_requires( void )
+{
+ if( requires ) { dlist_free( requires, __pkg_free_func ); requires = NULL; }
+}
+
+static void add_required( struct pkg *pkg )
+{
+ requires = dlist_append( requires, (void *)pkg );
+}
+
+///////////////////// only if we deside to print requires list
+//static void _print_requires( void *data, void *user_data )
+//{
+// struct pkg *pkg = (struct pkg *)data;
+//
+// if( pkg )
+// {
+// if( pkg->group )
+// fprintf( stderr, "%s/%s:%s\n", pkg->group, pkg->name, pkg->version );
+// else
+// fprintf( stderr, "%s:%s\n", pkg->name, pkg->version );
+// }
+//}
+//
+//static void print_requires( void )
+//{
+// dlist_foreach( requires, _print_requires, NULL );
+//}
+/////////////////////
+
+static struct package *package_alloc( void )
+{
+ struct package *package = NULL;
+
+ package = (struct package *)malloc( sizeof( struct package ) );
+ if( !package ) { FATAL_ERROR( "Cannot allocate memory" ); }
+ bzero( (void *)package, sizeof( struct package ) );
+
+ return package;
+}
+
+static void package_free( struct package *package )
+{
+ if( package )
+ {
+ if( package->name ) { free( package->name ); package->name = NULL; }
+ if( package->version ) { free( package->version ); package->version = NULL; }
+ if( package->group ) { free( package->group ); package->group = NULL; }
+
+ if( package->description ) { free( package->description ); package->description = NULL; }
+ if( package->tarball ) { free( package->tarball ); package->tarball = NULL; }
+
+ free( package );
+ }
+}
+
+static void __package_free_func( void *data, void *user_data )
+{
+ struct package *package = (struct package *)data;
+ if( package ) { package_free( package ); }
+}
+
+static void free_packages( void )
+{
+ if( packages ) { dlist_free( packages, __package_free_func ); packages = NULL; }
+}
+
+static void add_package( struct package *package )
+{
+ packages = dlist_append( packages, (void *)package );
+}
+
+////////////////////////////// just for testing
+//static void _print_packages( void *data, void *user_data )
+//{
+// struct package *package = (struct package *)data;
+//
+// if( package )
+// {
+// if( package->group )
+// fprintf( stderr, "%s/%s:%s:%s:%s:%s:%s\n", package->group, package->name, package->version, strproc( package->procedure ), strprio( package->priority, 0 ),
+// package->description, package->tarball );
+// else
+// fprintf( stderr, "%s:%s:%s:%s:%s:%s\n", package->name, package->version, strproc( package->procedure ), strprio( package->priority, 0 ),
+// package->description, package->tarball );
+// }
+//}
+//
+//static void print_packages( void )
+//{
+// dlist_foreach( packages, _print_packages, NULL );
+//}
+//////////////////////////////
+
+/*
+ End of package functions.
+ *********************************************/
+
+
+/*********************************************
+ Return status functions:
+ */
+static struct pkgrc *pkgrc_alloc( void )
+{
+ struct pkgrc *pkgrc = NULL;
+
+ pkgrc = (struct pkgrc *)malloc( sizeof( struct pkgrc ) );
+ if( !pkgrc ) { FATAL_ERROR( "Cannot allocate memory" ); }
+ bzero( (void *)pkgrc, sizeof( struct pkgrc ) );
+
+ return pkgrc;
+}
+
+static void pkgrc_free( struct pkgrc *pkgrc )
+{
+ if( pkgrc )
+ {
+ if( pkgrc->name ) { free( pkgrc->name ); pkgrc->name = NULL; }
+ if( pkgrc->version ) { free( pkgrc->version ); pkgrc->version = NULL; }
+ if( pkgrc->group ) { free( pkgrc->group ); pkgrc->group = NULL; }
+
+ free( pkgrc );
+ }
+}
+
+static void __pkgrc_free_func( void *data, void *user_data )
+{
+ struct pkgrc *pkgrc = (struct pkgrc *)data;
+ if( pkgrc ) { pkgrc_free( pkgrc ); }
+}
+
+static void free_pkgrcl( void )
+{
+ if( pkgrcl ) { dlist_free( pkgrcl, __pkgrc_free_func ); pkgrcl = NULL; }
+}
+
+static void add_pkgrc( struct pkgrc *pkgrc )
+{
+ pkgrcl = dlist_append( pkgrcl, (void *)pkgrc );
+}
+
+static struct pkgrc *find_pkgrc( struct dlist *list, pid_t pid )
+{
+ if( !list ) return NULL;
+
+ while( list && list->data )
+ {
+ if( ((struct pkgrc *)list->data)->pid == pid ) { return (struct pkgrc *)list->data; }
+ list = dlist_next( list );
+ }
+
+ return NULL;
+}
+
+static void __remove_success_pkgrc( void *data, void *user_data )
+{
+ struct pkgrc *pkgrc = (struct pkgrc *)data;
+
+ if( pkgrc && pkgrc->status == 0 )
+ {
+ pkgrcl = dlist_remove( pkgrcl, (const void *)data );
+ pkgrc_free( pkgrc );
+ }
+}
+
+static void cleanup_pkgrcl( void )
+{
+ dlist_foreach( pkgrcl, __remove_success_pkgrc, NULL );
+}
+
+static void _print_pkgrcl( void *data, void *user_data )
+{
+ struct pkgrc *pkgrc = (struct pkgrc *)data;
+
+ if( pkgrc )
+ {
+ if( pkgrc->group )
+ fprintf( stderr, "%s/%s:%s:%d\n", pkgrc->group, pkgrc->name, pkgrc->version, pkgrc->status );
+ else
+ fprintf( stderr, "%s:%s:%d\n", pkgrc->name, pkgrc->version, pkgrc->status );
+ }
+}
+
+static void return_codes_list( void )
+{
+ if( pkgrcl )
+ {
+ dlist_foreach( pkgrcl, _print_pkgrcl, NULL );
+ }
+}
+/*
+ End of return status functions.
+ *********************************************/
+
+
+/*******************************
+ remove spaces at end of line:
+ */
+//static void skip_eol_spaces( char *s )
+//{
+// char *p = (char *)0;
+//
+// if( !s || *s == '\0' ) return;
+//
+// p = s + strlen( s ) - 1;
+// while( isspace( *p ) ) { *p-- = '\0'; }
+//}
+//
+//static char *skip_lead_spaces( char *s )
+//{
+// char *p = (char *)0;
+//
+// if( !s || *s == '\0' ) return p;
+//
+// p = s; while( isspace( *p ) ) { ++p; }
+//
+// return( p );
+//}
+
+static char *trim( char *s )
+{
+ char *p = (char *)0;
+
+ if( !s || *s == '\0' ) return p;
+
+ p = s + strlen( s ) - 1;
+ while( isspace( *p ) ) { *p-- = '\0'; }
+ p = s; while( isspace( *p ) ) { ++p; }
+
+ return( p );
+}
+
+
+static void read_pkglist_file( const char *fname )
+{
+ char *ln = NULL;
+ char *line = NULL;
+ int lnum = 0;
+
+ FILE *fp = NULL;
+
+ if( !fname || (*fname == '\0') ) return;
+
+ fp = fopen( fname, "r" );
+ if( !fp )
+ {
+ FATAL_ERROR( "Cannot open '%s' PKGLIST file", basename( (char *)fname ) );
+ }
+
+ line = (char *)malloc( (size_t)PATH_MAX );
+ if( !line ) { FATAL_ERROR( "Cannot allocate memory" ); }
+ bzero( (void *)line, PATH_MAX );
+
+ while( (ln = fgets( line, PATH_MAX, fp )) )
+ {
+ char *p = NULL;
+ char *name = NULL, *vers = NULL, *desc = NULL, *ball = NULL, *proc = NULL, *prio = NULL;
+
+ ++lnum;
+
+ ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */
+ ln = trim( ln ); /* remove leading and trailing spaces */
+
+ if( *ln == '#' )
+ {
+ if( !strncmp( ln, "# required:", 11 ) )
+ {
+ char *n = NULL, *v = NULL, *g = NULL, *q = NULL;
+ char *rq = ln + 11;
+ rq = trim( rq );
+
+ n = rq;
+ if( (q = index( (const char *)n, '/' )) ) { *q = '\0'; g = n; n = ++q; g = trim( g ); }
+ if( (q = index( (const char *)n, '=' )) ) { *q = '\0'; v = ++q; n = trim( n ); }
+ v = trim( v );
+
+ if( n && v )
+ {
+ struct pkg *pkg = pkg_alloc();
+
+ pkg->name = xstrdup( (const char *)n );
+ pkg->version = xstrdup( (const char *)v );
+ if( g )
+ pkg->group = xstrdup( (const char *)g );
+
+ add_required( pkg );
+ }
+ }
+ continue; /* skip commented lines */
+ }
+
+ name = ln;
+ if( (p = index( (const char *)name, ':' )) ) { *p = '\0'; vers = ++p; name = trim( name ); } else continue;
+ if( (p = index( (const char *)vers, ':' )) ) { *p = '\0'; desc = ++p; vers = trim( vers ); } else continue;
+ if( (p = index( (const char *)desc, ':' )) ) { *p = '\0'; ball = ++p; desc = trim( desc ); } else continue;
+ if( (p = index( (const char *)ball, ':' )) ) { *p = '\0'; proc = ++p; ball = trim( ball ); } else continue;
+ if( (p = index( (const char *)proc, ':' )) ) { *p = '\0'; prio = ++p; proc = trim( proc ); } else continue;
+ prio = trim( prio );
+
+ if( name && vers && desc && ball && proc && prio )
+ {
+ char *buf = NULL;
+ struct package *package = NULL;
+ char *group = index( (const char *)ball, '/' );
+ enum _priority priority = OPTIONAL;
+
+ /*******************
+ Package priority:
+ */
+ if( strlen( (const char*)prio ) > 2 )
+ {
+ char *match = NULL;
+
+ to_lowercase( prio );
+ if( (match = strstr( prio, "req" )) && match == prio ) {
+ priority = REQUIRED;
+ }
+ else if( (match = strstr( prio, "rec" )) && match == prio ) {
+ priority = RECOMMENDED;
+ }
+ else if( (match = strstr( prio, "opt" )) && match == prio ) {
+ priority = OPTIONAL;
+ }
+ else if( (match = strstr( prio, "sk" )) && match == prio ) {
+ priority = SKIP;
+ }
+ else {
+ FATAL_ERROR( "%s: %d: Unknown '%s' priority value", basename( pkglist_fname ), lnum, prio );
+ }
+ }
+ else
+ {
+ FATAL_ERROR( "%s: %d: Unknown '%s' priority value", basename( pkglist_fname ), lnum, prio );
+ }
+
+ if( priority > install_priority ) continue;
+
+ buf = (char *)malloc( (size_t)PATH_MAX );
+ if( !buf ) { FATAL_ERROR( "Cannot allocate memory" ); }
+ bzero( (void *)buf, PATH_MAX );
+
+ package = package_alloc();
+
+ package->name = xstrdup( (const char *)name );
+ package->version = xstrdup( (const char *)vers );
+ package->description = xstrdup( (const char *)desc );
+
+ (void)sprintf( buf, "%s/%s", (const char *)srcdir, (const char *)ball );
+ {
+ enum _input_type type = IFMT_UNKNOWN;
+ char uncompress = '\0';
+
+ type = check_package_file( &uncompress, (const char *)&buf[0] );
+ if( type != IFMT_PKG )
+ {
+ FATAL_ERROR( "Unknown format of '%s' package file", (const char *)&buf[0] );
+ }
+ }
+ package->tarball = xstrdup( (const char *)&buf[0] );
+
+ free( buf );
+
+ package->priority = priority;
+
+ /********************
+ Install procedure:
+ */
+ if( strlen( (const char*)proc ) > 5 )
+ {
+ char *match = NULL;
+
+ to_lowercase( proc );
+ if( (match = strstr( proc, "install" )) && match == proc ) {
+ package->procedure = INSTALL;
+ }
+ else if( (match = strstr( proc, "update" )) && match == proc ) {
+ package->procedure = UPDATE;
+ }
+ else {
+ FATAL_ERROR( "%s: %d: Unknown '%s' procedure value", basename( pkglist_fname ), lnum, proc );
+ }
+ }
+ else
+ {
+ FATAL_ERROR( "%s: %d: Unknown '%s' procedure value", basename( pkglist_fname ), lnum, proc );
+ }
+
+ if( group )
+ {
+ *group = '\0';
+ group = ball;
+ package->group = xstrdup( (const char *)group );
+ }
+
+ add_package( package );
+ }
+
+ } /* End of while( ln = fgets( line ) ) */
+
+ free( line );
+ fclose( fp );
+}
+
+
+static void check_requires( void )
+{
+ if( requires )
+ {
+ exit_status = 1;
+
+ fprintf( stdout, "\nThe input '%s' has the list of %d required packages.\n\n", basename( pkglist_fname ), dlist_length( requires ) );
+
+ if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); }
+ free_resources();
+ exit( exit_status );
+ }
+}
+
+#if defined( HAVE_DIALOG )
+static DIALOG_LISTITEM *alloc_select_items( void )
+{
+ DIALOG_LISTITEM *items = NULL;
+ int i = 0, num = dlist_length( packages );
+ struct dlist *list = packages, *next = NULL;
+
+ items = (DIALOG_LISTITEM *)malloc( (num + 1) * sizeof(DIALOG_LISTITEM) );
+ if( !items ) { FATAL_ERROR( "Cannot allocate memory" ); }
+ bzero( (void *)items, (num + 1) * sizeof(DIALOG_LISTITEM) );
+
+ while( list )
+ {
+ struct package *package = NULL;
+
+ next = dlist_next( list );
+ package = (struct package *)list->data;
+ if( package )
+ {
+#define COLUMN_LENGHT 23 /* 22 symbols for name + " [UP] " */
+#define NAME_LENGHT (COLUMN_LENGHT - 7) /* strlen(" [UP] ") + 1; */
+#define UPDATE_SUFFIX " [UP] "
+#define INSTALL_SUFFIX " [in] "
+
+ char *name = (char *)malloc( (size_t)COLUMN_LENGHT );
+ if( !name ) { FATAL_ERROR( "Cannot allocate memory" ); }
+ bzero( (void *)name, (size_t)COLUMN_LENGHT );
+
+ if( strlen( package->name ) > (size_t)NAME_LENGHT )
+ {
+ (void)strncpy( name, (const char *)package->name, NAME_LENGHT - 2 );
+ (void)strcat( name, ".." );
+ }
+ else
+ {
+ (void)strcpy( name, (const char *)package->name );
+ }
+
+ if( package->procedure == UPDATE )
+ {
+ int p = strlen( name );
+ while( p < NAME_LENGHT ) { name[p] = ' '; ++p; }
+ name[p] = '\0';
+ (void)strcat( name, UPDATE_SUFFIX );
+ }
+ else
+ {
+ int p = strlen( name );
+ /* while( p < (COLUMN_LENGHT - 1) ) { name[p] = ' '; ++p; } */
+ while( p < NAME_LENGHT ) { name[p] = ' '; ++p; }
+ name[p] = '\0';
+ (void)strcat( name, INSTALL_SUFFIX );
+ }
+
+ items[i].name = name;
+ items[i].text = xstrdup( (const char *)package->description );
+ if( package->group )
+ items[i].help = xstrdup( (const char *)package->group );
+ items[i].state = 1;
+ }
+ ++i;
+ list = next;
+ }
+ return items;
+}
+
+static void free_select_items( DIALOG_LISTITEM *items )
+{
+ DIALOG_LISTITEM *p = items;
+
+ if( !p ) return;
+
+ while( p->name ) { free( p->name ); free( p->text ); if( p->help ) free( p->help ); ++p; }
+
+ free( items );
+}
+
+static void remove_unselected_packages( DIALOG_LISTITEM *items )
+{
+ DIALOG_LISTITEM *p = items;
+ struct dlist *rem = NULL, *list = NULL, *next = NULL;
+ int n = 0;
+
+ if( !p ) return;
+
+ while( p->name )
+ {
+ /* copy packages list item to the local list: */
+ if( !p->state ) { list = dlist_append( list, dlist_nth_data( packages, n ) ); }
+ ++p; ++n;
+ }
+
+ /****************************************************
+ remove items of the local list from packages list:
+ */
+ rem = list;
+ while( rem )
+ {
+ next = dlist_next( rem );
+ packages = dlist_remove( packages, rem->data );
+ rem = next;
+ }
+
+ /***************************
+ free unselected packages:
+ */
+ dlist_free( list, __package_free_func );
+}
+#endif
+
+
+/*********************************************
+ Progress functions:
+ */
+static void show_install_con_progress( const char *title, int percent )
+{
+#define GAUGE_LENGTH 68
+ size_t prefix = strlen( title ) + 2; /* title + ' [' */
+ size_t suffix = 6; /* '] 100%' */
+ size_t length = prefix + GAUGE_LENGTH + suffix;
+ int i, ctx = GAUGE_LENGTH * percent / 100;
+
+ if( percent < 0 ) percent = 0;
+ if( percent > 100 ) percent = 100;
+
+ printf( "\033[1A" ); /* move the cursor up 1 line */
+ printf( "\033[%dD", (int)length ); /* move cursor to start of line */
+ printf( "%s [", title );
+
+ for( i = 0; i < ctx; ++i ) { fprintf( stdout, "\u2588" ); }
+ for( ; i < GAUGE_LENGTH; ++i ) { fprintf( stdout, " " ); }
+
+ printf( "] %3d%%\n", percent );
+ fflush( stdout );
+}
+
+static void show_progress( void )
+{
+ const char *title = "Install:";
+ const char *message = "\n"
+ "Please wait for install all specified packages:\n"
+ "\n\n";
+
+ if( install_mode != CONSOLE )
+ {
+#if defined( HAVE_DIALOG )
+ show_install_dlg_progress( 0 );
+#else
+ fprintf( stdout, "%s", message );
+ show_install_con_progress( title, 0 );
+#endif
+ }
+ else
+ {
+ fprintf( stdout, "%s", message );
+ show_install_con_progress( title, 0 );
+ }
+
+}
+
+static void update_progress( int percent )
+{
+ const char *title = "Install:";
+
+ if( install_mode != CONSOLE )
+ {
+#if defined( HAVE_DIALOG )
+ show_install_dlg_progress( percent );
+#else
+ show_install_con_progress( title, percent );
+#endif
+ }
+ else
+ {
+ show_install_con_progress( title, percent );
+ }
+}
+
+static void stop_progress( void )
+{
+ const char *title = "Install:";
+
+ if( install_mode != CONSOLE )
+ {
+#if defined( HAVE_DIALOG )
+ show_install_dlg_progress( 100 );
+#else
+ show_install_con_progress( title, 100 );
+ fprintf( stdout, "\n" );
+#endif
+ }
+ else
+ {
+ show_install_con_progress( title, 100 );
+ fprintf( stdout, "\n" );
+ }
+}
+/*
+ End of progress functions.
+ *********************************************/
+
+
+
+/*********************************************
+ Install functions.
+ */
+static void install_package( struct package *package )
+{
+ int len = 0;
+ char *cmd = NULL, *opt = NULL, *out = "> /dev/null 2>&1";
+
+ if( !package ) return;
+
+ opt = (char *)malloc( (size_t)PATH_MAX );
+ if( !opt ) { FATAL_ERROR( "Cannot allocate memory" ); }
+ bzero( (void *)opt, PATH_MAX );
+ opt[0] = ' ';
+
+ if( gpgck ) (void)sprintf( opt, "--gpg-verify " );
+ if( (install_mode != CONSOLE) && !parallel && !progress ) (void)strcat( opt, "--info-dialog " );
+ if( parallel ) (void)strcat( opt, "--ignore-chrefs-errors " );
+ if( (install_mode == CONSOLE) && !parallel && !progress ) out = " ";
+
+ cmd = (char *)malloc( (size_t)PATH_MAX );
+ if( !cmd ) { FATAL_ERROR( "Cannot allocate memory" ); }
+ bzero( (void *)cmd, PATH_MAX );
+
+ len = snprintf( &cmd[0], PATH_MAX,
+ "%s/%s-package %s --priority=%s --root=%s %s %s",
+ selfdir, strproc( package->procedure ), opt,
+ strprio( package->priority, 1 ),
+ root, package->tarball, out );
+ if( len == 0 || len == PATH_MAX - 1 )
+ {
+ FATAL_ERROR( "Cannot install %s-%s package", package->name, package->version );
+ }
+ if( parallel )
+ {
+ struct pkgrc *pkgrc = pkgrc_alloc();
+
+ pkgrc->name = xstrdup( (const char *)package->name );
+ pkgrc->version = xstrdup( (const char *)package->version );
+ if( package->group )
+ pkgrc->group = xstrdup( (const char *)package->group );
+ pkgrc->pid = sys_exec_command( cmd );
+
+ add_pkgrc( pkgrc );
+ ++__child;
+ }
+ else
+ {
+ pid_t p = (pid_t) -1;
+ int rc = 0;
+
+ p = sys_exec_command( cmd );
+ rc = sys_wait_command( p, (char *)NULL, PATH_MAX );
+ if( rc != 0 && rc != 31 )
+ {
+ FATAL_ERROR( "Cannot install '%s-%s' package", package->name, package->version );
+ }
+ ++__successful;
+ }
+
+ free( cmd );
+ free( opt );
+}
+
+static void _serial_install_package( void *data, void *user_data )
+{
+ struct package *package = (struct package *)data;
+ int percent;
+
+ if( package ) { install_package( package ); }
+
+ if( progress )
+ {
+ percent = ( __successful < __all ) ? __successful * 100 / __all : 100;
+ update_progress( percent );
+ }
+}
+
+static void serial_install_packages( void )
+{
+ if( progress )
+ {
+ show_progress();
+ }
+
+ dlist_foreach( packages, _serial_install_package, NULL );
+
+ if( progress )
+ {
+ stop_progress();
+ }
+}
+
+
+static void *install_process( void *args )
+{
+ struct dlist *list = packages, *next = NULL;
+
+ int nstreams = ncpus * 2; /* two concurents for CPU */
+
+ while( list )
+ {
+ struct package *package = NULL;
+
+ next = dlist_next( list );
+ package = (struct package *)list->data;
+ if( package )
+ {
+ install_package( package );
+ }
+ list = next;
+
+ /* wait for available CPU: */
+ while( (__child - __terminated) > nstreams ) usleep( WAIT_USEC_FOR_CHILD );
+ }
+
+ return NULL;
+}
+
+static void parallel_install_packages( void )
+{
+ pthread_t install_process_id;
+ int status;
+
+ /* Start the parallel installation process: */
+ status = pthread_create( &install_process_id, NULL, install_process, NULL );
+ if( status != 0 )
+ {
+ FATAL_ERROR( "Cannot start parallel installation process" );
+ }
+ (void)pthread_detach( install_process_id );
+}
+
+/*
+ End of install functions.
+ *********************************************/
+
+
+
+
+static void dialogrc( void )
+{
+ struct stat st;
+ char *tmp = NULL;
+
+ tmp = (char *)malloc( (size_t)PATH_MAX );
+ if( !tmp ) { FATAL_ERROR( "Cannot allocate memory" ); }
+ bzero( (void *)tmp, PATH_MAX );
+
+ /* imagine that the utility is in /sbin directory: */
+ (void)sprintf( &tmp[0], "%s/../usr/share/%s/.dialogrc", selfdir, PACKAGE_NAME );
+ if( stat( (const char *)&tmp[0], &st ) == -1 )
+ {
+ /* finaly assume that /usr/sbin is a sbindir: */
+ (void)sprintf( &tmp[0], "%s/../../usr/share/%s/.dialogrc", selfdir, PACKAGE_NAME );
+ }
+
+ setenv( "DIALOGRC", (const char *)&tmp[0], 1 );
+
+ free( tmp );
+}
+
+static char *get_curdir( void )
+{
+ char *cwd = NULL;
+
+ cwd = (char *)malloc( PATH_MAX );
+ if( !cwd ) { FATAL_ERROR( "Cannot allocate memory" ); }
+ bzero( (void *)cwd, PATH_MAX );
+
+ if( getcwd( cwd, (size_t)PATH_MAX ) != NULL )
+ {
+ char *p = NULL;
+ remove_trailing_slash( cwd );
+ p = xstrdup( (const char *)cwd );
+ free( cwd );
+ return p;
+ }
+ else
+ {
+ FATAL_ERROR( "Cannot get absolute path to current directory" );
+ }
+
+ return (char *)NULL;
+}
+
+
+/*********************************************
+ Get directory where this program is placed:
+ */
+char *get_selfdir( void )
+{
+ char *buf = NULL;
+ ssize_t len;
+
+ buf = (char *)malloc( PATH_MAX );
+ if( !buf )
+ {
+ FATAL_ERROR( "Cannot allocate memory" );
+ }
+
+ bzero( (void *)buf, PATH_MAX );
+ len = readlink( "/proc/self/exe", buf, (size_t)PATH_MAX );
+ if( len > 0 && len < PATH_MAX )
+ {
+ char *p = xstrdup( (const char *)dirname( buf ) );
+ free( buf );
+ return p;
+ }
+ FATAL_ERROR( "Cannot determine self directory. Please mount /proc filesystem" );
+}
+
+void set_stack_size( void )
+{
+ const rlim_t stack_size = 16 * 1024 * 1024; /* min stack size = 16 MB */
+ struct rlimit rl;
+ int ret;
+
+ ret = getrlimit( RLIMIT_STACK, &rl );
+ if( ret == 0 )
+ {
+ if( rl.rlim_cur < stack_size )
+ {
+ rl.rlim_cur = stack_size;
+ ret = setrlimit( RLIMIT_STACK, &rl );
+ if( ret != 0 )
+ {
+ fprintf(stderr, "setrlimit returned result = %d\n", ret);
+ FATAL_ERROR( "Cannot set stack size" );
+ }
+ }
+ }
+}
+
+
+int main( int argc, char *argv[] )
+{
+ gid_t gid;
+
+ set_signal_handlers();
+
+ gid = getgid();
+ setgroups( 1, &gid );
+
+ ncpus = get_nprocs();
+
+ fatal_error_hook = fatal_error_actions;
+
+ selfdir = get_selfdir();
+ curdir = get_curdir();
+ dialogrc();
+
+ errlog = stderr;
+
+ program = basename( argv[0] );
+ get_args( argc, argv );
+
+ /* set_stack_size(); */
+
+ tmpdir = _mk_tmpdir();
+ if( !tmpdir )
+ {
+ FATAL_ERROR( "Cannot create temporary directory" );
+ }
+
+ /***********************************************************
+ Fill requires and packages lists, check package tarballs,
+ skip unnesessary packages according to --priority option:
+ */
+ read_pkglist_file( (const char *)pkglist_fname );
+
+ /* check only the list of requires in the input PKGLIST: */
+ if( rqck ) check_requires();
+
+#if defined( HAVE_DIALOG )
+ if( install_mode == MENUDIALOG )
+ {
+ int status = 0, num = dlist_length( packages );
+ DIALOG_LISTITEM *items = alloc_select_items();
+
+ status = select_packages_box( items, num, 0, 0 );
+ if( !status )
+ {
+ remove_unselected_packages( items );
+ free_select_items( items );
+ }
+ else
+ {
+ /* Abort installation: */
+ if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); }
+ free_resources();
+
+ exit( exit_status );
+ }
+ }
+#endif
+
+
+ if( parallel )
+ {
+ /************************
+ parallel installation:
+ */
+ int percent = 0;
+
+ __all = dlist_length( packages );
+ __done = 0; __child = 0;
+
+ __terminated = 0; __successful = 0;
+
+ show_progress();
+
+ parallel_install_packages();
+
+ if( __terminated < __all )
+ {
+ while( !__done )
+ {
+ percent = ( __terminated < __all ) ? __terminated * 100 / __all : 100;
+
+ update_progress( percent );
+ usleep( WAIT_USEC_FOR_CHILD );
+ }
+ }
+
+ __done = 0; __child = 0;
+
+ stop_progress();
+
+ if( __successful < __terminated ) { percent = __successful * 100 / __terminated; }
+ else { percent = 100; }
+
+ __terminated = 0; __successful = 0;
+
+ if( install_mode != CONSOLE )
+ {
+#if defined( HAVE_DIALOG )
+ char *msg = NULL;
+
+ msg = (char *)malloc( (size_t)80 );
+ if( !msg ) { FATAL_ERROR( "Cannot allocate memory" ); }
+ bzero( (void *)msg, 80 );
+
+ (void)sprintf( msg, "\nSuccessfully installed %d%% of %d specified packages.\n", percent, __all );
+
+ (void)info_box( " \\Z0INSTALL PACKAGES\\Zn ", msg, 5, 0, 0 );
+
+ free( msg );
+#else
+ fprintf( stdout, "\nSuccessfully installed %d%% of %d specified packages.\n\n", percent, __all );
+#endif
+ }
+ else
+ {
+ fprintf( stdout, "\nSuccessfully installed %d%% of %d specified packages.\n\n", percent, __all );
+ }
+
+ cleanup_pkgrcl(); /* remove successfully installed packages from return codes list */
+ if( pkgrcl && error_pkgs_list )
+ {
+ return_codes_list();
+ }
+ }
+ else
+ {
+ /**********************
+ serial installation:
+ */
+
+ __successful = 0;
+ __all = dlist_length( packages );
+
+ serial_install_packages();
+
+ if( install_mode != CONSOLE )
+ {
+#if defined( HAVE_DIALOG )
+ info_box( " \\Z4INSTALL PACKAGES\\Zn ",
+ "\nAll of specified packages have been installed.\n", 5, 0, 0 );
+#else
+ fprintf( stdout, "\nAll of specified packages have been installed.\n\n" );
+#endif
+ }
+ else
+ {
+ fprintf( stdout, "\nAll of specified packages have been installed.\n\n" );
+ }
+
+ }
+
+
+ if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); }
+ free_resources();
+
+ exit( exit_status );
+}