Radix cross Linux Package Tools

Package Tools – is a set of utilities to create, install, and update RcL packages

8 Commits   0 Branches   2 Tags
     5         kx 
     5         kx /**********************************************************************
     5         kx 
     5         kx   Copyright 2019 Andrey V.Kosteltsev
     5         kx 
     5         kx   Licensed under the Radix.pro License, Version 1.0 (the "License");
     5         kx   you may not use this file  except  in compliance with the License.
     5         kx   You may obtain a copy of the License at
     5         kx 
     5         kx      https://radix.pro/licenses/LICENSE-1.0-en_US.txt
     5         kx 
     5         kx   Unless required by applicable law or agreed to in writing, software
     5         kx   distributed under the License is distributed on an "AS IS" BASIS,
     5         kx   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
     5         kx   implied.
     5         kx 
     5         kx  **********************************************************************/
     5         kx 
     5         kx #include <config.h>
     5         kx 
     5         kx #include <stdlib.h>
     5         kx #include <stdio.h>
     5         kx #include <sys/types.h>
     5         kx #include <stdint.h>
     5         kx #include <dirent.h>
     5         kx #include <sys/stat.h> /* chmod(2)    */
     5         kx #include <sys/file.h> /* flock(2)    */
     5         kx #include <fcntl.h>
     5         kx #include <linux/limits.h>
     5         kx #include <alloca.h>   /* alloca(3)   */
     5         kx #include <string.h>   /* strdup(3)   */
     5         kx #include <strings.h>  /* index(3)    */
     5         kx #include <libgen.h>   /* basename(3) */
     5         kx #include <ctype.h>    /* tolower(3)  */
     5         kx #include <errno.h>
     5         kx #include <time.h>
     5         kx #include <sys/time.h>
     5         kx #include <pwd.h>
     5         kx #include <grp.h>
     5         kx #include <stdarg.h>
     5         kx #include <unistd.h>
     5         kx 
     5         kx #include <sys/wait.h>
     5         kx 
     5         kx #include <sys/resource.h>
     5         kx 
     5         kx #include <signal.h>
     5         kx #if !defined SIGCHLD && defined SIGCLD
     5         kx # define SIGCHLD SIGCLD
     5         kx #endif
     5         kx 
     5         kx #define _GNU_SOURCE
     5         kx #include <getopt.h>
     5         kx 
     5         kx #include <msglog.h>
     5         kx #include <wrapper.h>
     5         kx #include <system.h>
     5         kx #include <cmpvers.h>
     5         kx 
     5         kx #define PROGRAM_NAME "check-package"
     5         kx 
     5         kx #include <defs.h>
     5         kx 
     5         kx 
     5         kx char *program = PROGRAM_NAME;
     5         kx char *root = NULL, *pkgs_path = NULL, *pkg_fname = NULL, *pkg_found = NULL,
     5         kx      *tmpdir = NULL;
     5         kx 
     5         kx int   quiet = 0, print_broken_files = 0;
     5         kx 
     5         kx int   exit_status = EXIT_SUCCESS; /* errors counter */
     5         kx char *selfdir     = NULL;
     5         kx 
     5         kx static char           *pkgname = NULL,
     5         kx                        *pkgver = NULL,
     5         kx                          *arch = NULL,
     5         kx                    *distroname = NULL,
     5         kx                     *distrover = NULL,
     5         kx                         *group = NULL;
     5         kx 
     5         kx static char *installed_version = NULL;
     5         kx 
     5         kx enum _input_type {
     5         kx   IFMT_PKG = 0,
     5         kx   IFMT_LOG,
     5         kx 
     5         kx   IFMT_UNKNOWN
     5         kx } input_format = IFMT_PKG;
     5         kx 
     5         kx 
     5         kx #define FREE_PKGINFO_VARIABLES() \
     5         kx   if( pkgname )           { free( pkgname );           } pkgname = NULL;            \
     5         kx   if( pkgver )            { free( pkgver );            } pkgver = NULL;             \
     5         kx   if( arch )              { free( arch );              } arch = NULL;               \
     5         kx   if( distroname )        { free( distroname );        } distroname = NULL;         \
     5         kx   if( distrover )         { free( distrover );         } distrover = NULL;          \
     5         kx   if( group )             { free( group );             } group = NULL;              \
     5         kx   if( installed_version ) { free( installed_version ); } installed_version = NULL
     5         kx 
     5         kx void free_resources()
     5         kx {
     5         kx   if( root )         { free( root );         root         = NULL; }
     5         kx   if( pkgs_path )    { free( pkgs_path );    pkgs_path    = NULL; }
     5         kx   if( pkg_fname )    { free( pkg_fname );    pkg_fname    = NULL; }
     5         kx 
     5         kx   if( selfdir )      { free( selfdir );      selfdir      = NULL; }
     5         kx 
     5         kx   FREE_PKGINFO_VARIABLES();
     5         kx }
     5         kx 
     5         kx void usage()
     5         kx {
     5         kx   free_resources();
     5         kx 
     5         kx   fprintf( stdout, "\n" );
     5         kx   fprintf( stdout, "Usage: %s [options] <package|pkglog|pkgname>\n", program );
     5         kx   fprintf( stdout, "\n" );
     5         kx   fprintf( stdout, "This utility checks if specified package is installed.  If package\n" );
     5         kx   fprintf( stdout, "or some another version  of this package is already installed then\n" );
     5         kx   fprintf( stdout, "this utility checks if installed package is correct.\n" );
     5         kx   fprintf( stdout, "\n" );
     5         kx   fprintf( stdout, "Options:\n" );
     5         kx   fprintf( stdout, "  -h,--help                     Display this information.\n" );
     5         kx   fprintf( stdout, "  -v,--version                  Display the version of %s utility.\n", program );
     5         kx   fprintf( stdout, "  -p,--print-broken-files       Print the list of broken directories,\n" );
     5         kx   fprintf( stdout, "                                files, or symbolic links to stdout.\n" );
     5         kx   fprintf( stdout, "  -q,--quiet                    Do not display explanations for\n" );
     5         kx   fprintf( stdout, "                                return codes.\n" );
     5         kx   fprintf( stdout, "  -r,--root=<DIR>               Target rootfs path.\n" );
     5         kx   fprintf( stdout, "\n" );
     5         kx   fprintf( stdout, "Parameter:\n" );
     5         kx   fprintf( stdout, "  <package|pkglog|pkgname>      The PKGNAME, PACKAGE tarball or PKGLOG.\n"  );
     5         kx   fprintf( stdout, "\n" );
     5         kx   fprintf( stdout, "Return codes:\n" );
     5         kx   fprintf( stdout, "  ------+---------------------------+--------------------\n"  );
     5         kx   fprintf( stdout, "   code |          status           | what can be done\n"  );
     5         kx   fprintf( stdout, "  ------+---------------------------+--------------------\n"  );
     5         kx   fprintf( stdout, "     30 | not installed             | install\n"  );
     5         kx   fprintf( stdout, "     31 | installed correctly       | nothing to do\n"  );
     5         kx   fprintf( stdout, "     32 | installed but not correct | repair, re-install\n"  );
     5         kx   fprintf( stdout, "     33 | installed correctly       | upgrade\n"  );
     5         kx   fprintf( stdout, "     34 | installed but not correct | repair, upgrade\n"  );
     5         kx   fprintf( stdout, "     35 | installed correctly       | downgrade\n"  );
     5         kx   fprintf( stdout, "     36 | installed but not correct | repair, downgrade\n"  );
     5         kx   fprintf( stdout, "  ------+---------------------------+--------------------\n"  );
     5         kx   fprintf( stdout, "\n" );
     5         kx   fprintf( stdout, "Other non-zero codes assumes that this utility faced to errors not\n" );
     5         kx   fprintf( stdout, "related to quality of installed package.\n" );
     5         kx   fprintf( stdout, "\n" );
     5         kx   fprintf( stdout, "If package specified by short name (probably with version) instead\n" );
     5         kx   fprintf( stdout, "of regular files such as package tarball or pkglog file  then this\n" );
     5         kx   fprintf( stdout, "utility doesn't check other installed versions of this package.\n" );
     5         kx   fprintf( stdout, "\n" );
     5         kx 
     5         kx   exit( EXIT_FAILURE );
     5         kx }
     5         kx 
     5         kx void to_lowercase( char *s )
     5         kx {
     5         kx   char *p = s;
     5         kx   while( p && *p ) { int c = *p; *p = tolower( c ); ++p; }
     5         kx }
     5         kx 
     5         kx void to_uppercase( char *s )
     5         kx {
     5         kx   char *p = s;
     5         kx   while( p && *p ) { int c = *p; *p = toupper( c ); ++p; }
     5         kx }
     5         kx 
     5         kx void version()
     5         kx {
     5         kx   char *upper = NULL;
     5         kx 
     5         kx   upper = (char *)alloca( strlen( program ) + 1 );
     5         kx 
     5         kx   strcpy( (char *)upper, (const char *)program );
     5         kx   to_uppercase( upper );
     5         kx 
     5         kx   fprintf( stdout, "%s (%s) %s\n", program, upper, PROGRAM_VERSION );
     5         kx 
     5         kx   fprintf( stdout, "Copyright (C) 2019 Andrey V.Kosteltsev.\n" );
     5         kx   fprintf( stdout, "This is free software.   There is NO warranty; not even\n" );
     5         kx   fprintf( stdout, "for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n" );
     5         kx   fprintf( stdout, "\n" );
     5         kx 
     5         kx   free_resources();
     5         kx   exit( EXIT_SUCCESS );
     5         kx }
     5         kx 
     5         kx 
     5         kx static void remove_trailing_slash( char *dir )
     5         kx {
     5         kx   char *s;
     5         kx 
     5         kx   if( !dir || dir[0] == '\0' ) return;
     5         kx 
     5         kx   s = dir + strlen( dir ) - 1;
     5         kx   while( *s == '/' )
     5         kx   {
     5         kx     *s = '\0'; --s;
     5         kx   }
     5         kx }
     5         kx 
     5         kx 
     5         kx static int _mkdir_p( const char *dir, const mode_t mode )
     5         kx {
     5         kx   char  *buf;
     5         kx   char  *p = NULL;
     5         kx   struct stat sb;
     5         kx 
     5         kx   if( !dir ) return -1;
     5         kx 
     5         kx   buf = (char *)alloca( strlen( dir ) + 1 );
     5         kx   strcpy( buf, dir );
     5         kx 
     5         kx   remove_trailing_slash( buf );
     5         kx 
     5         kx   /* check if path exists and is a directory */
     5         kx   if( stat( buf, &sb ) == 0 )
     5         kx   {
     5         kx     if( S_ISDIR(sb.st_mode) )
     5         kx     {
     5         kx       return 0;
     5         kx     }
     5         kx   }
     5         kx 
     5         kx   /* mkdir -p */
     5         kx   for( p = buf + 1; *p; ++p )
     5         kx   {
     5         kx     if( *p == '/' )
     5         kx     {
     5         kx       *p = 0;
     5         kx       /* test path */
     5         kx       if( stat( buf, &sb ) != 0 )
     5         kx       {
     5         kx         /* path does not exist - create directory */
     5         kx         if( mkdir( buf, mode ) < 0 )
     5         kx         {
     5         kx           return -1;
     5         kx         }
     5         kx       } else if( !S_ISDIR(sb.st_mode) )
     5         kx       {
     5         kx         /* not a directory */
     5         kx         return -1;
     5         kx       }
     5         kx       *p = '/';
     5         kx     }
     5         kx   }
     5         kx 
     5         kx   /* test path */
     5         kx   if( stat( buf, &sb ) != 0 )
     5         kx   {
     5         kx     /* path does not exist - create directory */
     5         kx     if( mkdir( buf, mode ) < 0 )
     5         kx     {
     5         kx       return -1;
     5         kx     }
     5         kx   } else if( !S_ISDIR(sb.st_mode) )
     5         kx   {
     5         kx     /* not a directory */
     5         kx     return -1;
     5         kx   }
     5         kx 
     5         kx   return 0;
     5         kx }
     5         kx 
     5         kx static void _rm_tmpdir( const char *dirpath )
     5         kx {
     5         kx   DIR    *dir;
     5         kx   char   *path;
     5         kx   size_t  len;
     5         kx 
     5         kx   struct stat    path_sb, entry_sb;
     5         kx   struct dirent *entry;
     5         kx 
     5         kx   if( stat( dirpath, &path_sb ) == -1 )
     5         kx   {
     5         kx     return; /* stat returns error code; errno is set */
     5         kx   }
     5         kx 
     5         kx   if( S_ISDIR(path_sb.st_mode) == 0 )
     5         kx   {
     5         kx     return; /* dirpath is not a directory */
     5         kx   }
     5         kx 
     5         kx   if( (dir = opendir(dirpath) ) == NULL )
     5         kx   {
     5         kx     return; /* Cannot open direcroty; errno is set */
     5         kx   }
     5         kx 
     5         kx   len = strlen( dirpath );
     5         kx 
     5         kx   while( (entry = readdir( dir )) != NULL)
     5         kx   {
     5         kx 
     5         kx     /* skip entries '.' and '..' */
     5         kx     if( ! strcmp( entry->d_name, "." ) || ! strcmp( entry->d_name, ".." ) ) continue;
     5         kx 
     5         kx     /* determinate a full name of an entry */
     5         kx     path = alloca( len + strlen( entry->d_name ) + 2 );
     5         kx     strcpy( path, dirpath );
     5         kx     strcat( path, "/" );
     5         kx     strcat( path, entry->d_name );
     5         kx 
     5         kx     if( stat( path, &entry_sb ) == 0 )
     5         kx     {
     5         kx       if( S_ISDIR(entry_sb.st_mode) )
     5         kx       {
     5         kx         /* recursively remove a nested directory */
     5         kx         _rm_tmpdir( path );
     5         kx       }
     5         kx       else
     5         kx       {
     5         kx         /* remove a file object */
     5         kx         (void)unlink( path );
     5         kx       }
     5         kx     }
     5         kx     /* else { stat() returns error code; errno is set; and we have to continue the loop } */
     5         kx 
     5         kx   }
     5         kx 
     5         kx   /* remove the devastated directory and close the object of this directory */
     5         kx   (void)rmdir( dirpath );
     5         kx 
     5         kx   closedir( dir );
     5         kx }
     5         kx 
     5         kx static char *_mk_tmpdir( void )
     5         kx {
     5         kx   char   *buf = NULL, *p, *tmp = "/tmp";
     5         kx   size_t  len = 0, size = 0;
     5         kx 
     5         kx   (void)umask( S_IWGRP | S_IWOTH ); /* octal 022 */
     5         kx 
     5         kx   /* Get preferred directory for tmp files */
     5         kx   if( (p = getenv( "TMP" )) != NULL ) {
     5         kx     tmp = p;
     5         kx   }
     5         kx   else if( (p = getenv( "TEMP" )) != NULL ) {
     5         kx     tmp = p;
     5         kx   }
     5         kx 
     5         kx   size = strlen( tmp ) + strlen( DISTRO_NAME ) + strlen( program ) + 12;
     5         kx 
     5         kx   buf = (char *)malloc( size );
     5         kx   if( !buf ) return NULL;
     5         kx 
     5         kx   len = snprintf( buf, size, (const char *)"%s/%s/%s-%.7u", tmp, DISTRO_NAME, program, getpid() );
     5         kx   if( len == 0 || len == size - 1 )
     5         kx   {
     5         kx     free( buf ); return NULL;
     5         kx   }
     5         kx 
     5         kx   _rm_tmpdir( (const char *)&buf[0] );
     5         kx 
     5         kx   if( _mkdir_p( buf, S_IRWXU | S_IRWXG | S_IRWXO ) == 0 )
     5         kx   {
     5         kx     return buf;
     5         kx   }
     5         kx 
     5         kx   free( buf ); return NULL;
     5         kx }
     5         kx 
     5         kx 
     5         kx void fatal_error_actions( void )
     5         kx {
     5         kx   logmsg( errlog, MSG_NOTICE, "Free resources on FATAL error..." );
     5         kx   if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); }
     5         kx   free_resources();
     5         kx }
     5         kx 
     5         kx void sigint( int signum )
     5         kx {
     5         kx   (void)signum;
     5         kx 
     5         kx   if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); }
     5         kx   free_resources();
     5         kx }
     5         kx 
     5         kx static void set_signal_handlers()
     5         kx {
     5         kx   struct sigaction  sa;
     5         kx   sigset_t          set;
     5         kx 
     5         kx   memset( &sa, 0, sizeof( sa ) );
     5         kx   sa.sa_handler = sigint;          /* TERM, INT */
     5         kx   sa.sa_flags = SA_RESTART;
     5         kx   sigemptyset( &set );
     5         kx   sigaddset( &set, SIGTERM );
     5         kx   sigaddset( &set, SIGINT );
     5         kx   sa.sa_mask = set;
     5         kx   sigaction( SIGTERM, &sa, NULL );
     5         kx   sigaction( SIGINT, &sa,  NULL );
     5         kx 
     5         kx   memset( &sa, 0, sizeof( sa ) );  /* ignore SIGPIPE */
     5         kx   sa.sa_handler = SIG_IGN;
     5         kx   sa.sa_flags = 0;
     5         kx   sigaction( SIGPIPE, &sa, NULL );
     5         kx 
     5         kx   /* System V fork+wait does not work if SIGCHLD is ignored */
     5         kx   signal( SIGCHLD, SIG_DFL );
     5         kx }
     5         kx 
     5         kx 
     5         kx static enum _input_type check_input_file( char *uncompress, const char *fname )
     5         kx {
     5         kx   struct stat st;
     5         kx   size_t pkglog_size = 0;
     5         kx   unsigned char buf[8];
     5         kx   int rc, fd;
     5         kx 
     5         kx   /* SIGNATURES: https://www.garykessler.net/library/file_sigs.html */
     5         kx 
     5         kx   if( uncompress )
     5         kx   {
     5         kx     *uncompress = '\0';
     5         kx   }
     5         kx 
     5         kx   if( stat( fname, &st ) == -1 )
     5         kx   {
     5         kx     FATAL_ERROR( "Cannot access %s file: %s", basename( (char *)fname ), strerror( errno ) );
     5         kx   }
     5         kx 
     5         kx   pkglog_size = st.st_size;
     5         kx 
     5         kx   if( (fd = open( fname, O_RDONLY )) == -1 )
     5         kx   {
     5         kx     FATAL_ERROR( "Cannot open %s file: %s", basename( (char *)fname ), strerror( errno ) );
     5         kx   }
     5         kx 
     5         kx   rc = (int)read( fd, (void *)&buf[0], 7 );
     5         kx   if( rc != 7 )
     5         kx   {
     5         kx     close( fd ); return IFMT_UNKNOWN;
     5         kx   }
     5         kx   buf[7] = '\0';
     5         kx 
     5         kx   /* TEXT */
     5         kx   if( !strncmp( (const char *)&buf[0], "PACKAGE", 7 ) )
     5         kx   {
     5         kx     close( fd ); return IFMT_LOG;
     5         kx   }
     5         kx 
     5         kx   /* GZ */
     5         kx   if( buf[0] == 0x1F && buf[1] == 0x8B && buf[2] == 0x08 )
     5         kx   {
     5         kx     if( uncompress ) { *uncompress = 'x'; }
     5         kx     close( fd ); return IFMT_PKG;
     5         kx   }
     5         kx 
     5         kx   /* BZ2 */
     5         kx   if( buf[0] == 0x42 && buf[1] == 0x5A && buf[2] == 0x68 )
     5         kx   {
     5         kx     if( uncompress ) { *uncompress = 'j'; }
     5         kx     close( fd ); return IFMT_PKG;
     5         kx   }
     5         kx 
     5         kx   /* XZ */
     5         kx   if( buf[0] == 0xFD && buf[1] == 0x37 && buf[2] == 0x7A &&
     5         kx       buf[3] == 0x58 && buf[4] == 0x5A && buf[5] == 0x00   )
     5         kx   {
     5         kx     if( uncompress ) { *uncompress = 'J'; }
     5         kx     close( fd ); return IFMT_PKG;
     5         kx   }
     5         kx 
     5         kx   if( pkglog_size > 262 )
     5         kx   {
     5         kx     if( lseek( fd, 257, SEEK_SET ) == -1 )
     5         kx     {
     5         kx       FATAL_ERROR( "Cannot check signature of %s file: %s", basename( (char *)fname ), strerror( errno ) );
     5         kx     }
     5         kx     rc = (int)read( fd, &buf[0], 5 );
     5         kx     if( rc != 5 )
     5         kx     {
     5         kx       FATAL_ERROR( "Cannot read signature of %s file", basename( (char *)fname ) );
     5         kx     }
     5         kx     /* TAR */
     5         kx     if( buf[0] == 0x75 && buf[1] == 0x73 && buf[2] == 0x74 && buf[3] == 0x61 && buf[4] == 0x72 )
     5         kx     {
     5         kx       close( fd ); return IFMT_PKG;
     5         kx     }
     5         kx   }
     5         kx 
     5         kx   close( fd ); return IFMT_UNKNOWN;
     5         kx }
     5         kx 
     5         kx 
     5         kx void get_args( int argc, char *argv[] )
     5         kx {
     5         kx   const char* short_options = "hvpqr:";
     5         kx 
     5         kx   const struct option long_options[] =
     5         kx   {
     5         kx     { "help",               no_argument,       NULL, 'h' },
     5         kx     { "version",            no_argument,       NULL, 'v' },
     5         kx     { "print-broken-files", no_argument,       NULL, 'p' },
     5         kx     { "quiet",              no_argument,       NULL, 'q' },
     5         kx     { "root",               required_argument, NULL, 'r' },
     5         kx     { NULL,                 0,                 NULL,  0  }
     5         kx   };
     5         kx 
     5         kx   int ret;
     5         kx   int option_index = 0;
     5         kx 
     5         kx   while( (ret = getopt_long( argc, argv, short_options, long_options, &option_index )) != -1 )
     5         kx   {
     5         kx     switch( ret )
     5         kx     {
     5         kx       case 'h':
     5         kx       {
     5         kx         usage();
     5         kx         break;
     5         kx       }
     5         kx       case 'v':
     5         kx       {
     5         kx         version();
     5         kx         break;
     5         kx       }
     5         kx       case 'p':
     5         kx       {
     5         kx         print_broken_files = 1;
     5         kx         break;
     5         kx       }
     5         kx       case 'q':
     5         kx       {
     5         kx         quiet = 1;
     5         kx         break;
     5         kx       }
     5         kx 
     5         kx       case 'r':
     5         kx       {
     5         kx         if( optarg != NULL )
     5         kx         {
     5         kx           root = xstrdup( (const char *)optarg );
     5         kx           remove_trailing_slash( root );
     5         kx         }
     5         kx         else
     5         kx           /* option is present but without value */
     5         kx           usage();
     5         kx         break;
     5         kx       }
     5         kx 
     5         kx       case '?': default:
     5         kx       {
     5         kx         usage();
     5         kx         break;
     5         kx       }
     5         kx     }
     5         kx   }
     5         kx 
     5         kx 
     5         kx   if( optind < argc )
     5         kx   {
     5         kx     pkg_fname = xstrdup( (const char *)argv[optind] );
     5         kx   }
     5         kx   else
     5         kx   {
     5         kx     usage();
     5         kx   }
     5         kx 
     5         kx 
     5         kx   if( !pkgs_path )
     5         kx   {
     5         kx     struct stat st;
     5         kx     char  *buf = NULL;
     5         kx 
     5         kx     bzero( (void *)&st, sizeof( struct stat ) );
     5         kx 
     5         kx     buf = (char *)malloc( (size_t)PATH_MAX );
     5         kx     if( !buf ) { FATAL_ERROR( "Cannot allocate memory" ); }
     5         kx     bzero( (void *)buf, PATH_MAX );
     5         kx 
     5         kx     if( !root )
     5         kx     {
     5         kx       buf[0] = '/'; buf[1] = '\0';
     5         kx     }
     5         kx     else
     5         kx     {
     5         kx       int len = strlen( root );
     5         kx 
     5         kx       (void)strcpy( buf, (const char *)root );
     5         kx       if( buf[ len - 1 ] != '/' )
     5         kx       {
     5         kx         buf[len] = '/'; buf[len+1] = '\0';
     5         kx       }
     5         kx     }
     5         kx 
     5         kx     (void)strcat( buf, PACKAGES_PATH );
     5         kx     if( stat( (const char *)&buf[0], &st ) == -1 )
     5         kx     {
     5         kx       FATAL_ERROR( "Cannot access '%s' file or directory: %s", buf, strerror( errno ) );
     5         kx     }
     5         kx 
     5         kx     if( S_ISDIR(st.st_mode) )
     5         kx     {
     5         kx       pkgs_path = xstrdup( (const char *)&buf[0] );
     5         kx       free( buf );
     5         kx     }
     5         kx     else
     5         kx     {
     5         kx       FATAL_ERROR( "Defined --root '%s' is not a directory", buf );
     5         kx     }
     5         kx 
     5         kx   } /* End if( !pkgs_path ) */
     5         kx }
     5         kx 
     5         kx 
     5         kx /***********************************************************
     5         kx   Remove leading spaces and take non-space characters only:
     5         kx   (Especialy for pkginfo lines)
     5         kx  */
     5         kx static char *skip_spaces( char *s )
     5         kx {
     5         kx   char *q, *p = (char *)0;
     5         kx 
     5         kx   if( !s || *s == '\0' ) return p;
     5         kx 
     5         kx   p = s;
     5         kx 
     5         kx   while( (*p == ' ' || *p == '\t') && *p != '\0' ) { ++p; } q = p;
     5         kx   while(  *q != ' ' && *q != '\t'  && *q != '\0' ) { ++q; } *q = '\0';
     5         kx 
     5         kx   if( *p == '\0' ) return (char *)0;
     5         kx 
     5         kx   return( xstrdup( (const char *)p ) );
     5         kx }
     5         kx 
     5         kx 
     5         kx /*******************************
     5         kx   remove spaces at end of line:
     5         kx  */
     5         kx static void skip_eol_spaces( char *s )
     5         kx {
     5         kx   char *p = (char *)0;
     5         kx 
     5         kx   if( !s || *s == '\0' ) return;
     5         kx 
     5         kx   p = s + strlen( s ) - 1;
     5         kx   while( isspace( *p ) ) { *p-- = '\0'; }
     5         kx }
     5         kx 
     5         kx 
     5         kx /***************************************************************
     5         kx   Probe functions:
     5         kx  */
     5         kx static void _probe_pkglog( const char *dirpath, const char *grp )
     5         kx {
     5         kx   DIR    *dir;
     5         kx   char   *path;
     5         kx   size_t  len;
     5         kx 
     5         kx   struct stat    path_sb, entry_sb;
     5         kx   struct dirent *entry;
     5         kx 
     5         kx   if( pkg_found ) return;
     5         kx 
     5         kx   if( stat( dirpath, &path_sb ) == -1 )
     5         kx   {
     5         kx     FATAL_ERROR( "%s: Cannot stat Setup Database or destination directory", dirpath );
     5         kx   }
     5         kx 
     5         kx   if( S_ISDIR(path_sb.st_mode) == 0 )
     5         kx   {
     5         kx     FATAL_ERROR( "%s: Setup Database or destination is not a directory", dirpath );
     5         kx   }
     5         kx 
     5         kx   if( (dir = opendir(dirpath) ) == NULL )
     5         kx   {
     5         kx     FATAL_ERROR( "Canot access %s directory: %s", dirpath, strerror( errno ) );
     5         kx   }
     5         kx 
     5         kx   len = strlen( dirpath );
     5         kx 
     5         kx   while( (entry = readdir( dir )) != NULL)
     5         kx   {
     5         kx     /* skip entries '.' and '..' */
     5         kx     if( ! strcmp( entry->d_name, "." ) || ! strcmp( entry->d_name, ".." ) ) continue;
     5         kx 
     5         kx     /* determinate a full name of an entry */
     5         kx     path = alloca( len + strlen( entry->d_name ) + 2 );
     5         kx 
     5         kx     strcpy( path, dirpath );
     5         kx     strcat( path, "/" );
     5         kx     strcat( path, entry->d_name );
     5         kx 
     5         kx     if( stat( path, &entry_sb ) == 0 )
     5         kx     {
     5         kx       if( S_ISREG(entry_sb.st_mode) )
     5         kx       {
     5         kx         char *match  = NULL;
     5         kx         char *pkglog = basename( path );
     5         kx 
     5         kx         if( (match = strstr( pkglog, (const char *)basename( pkg_fname ) )) && match == pkglog )
     5         kx         {
     5         kx           char *buf = NULL, *p = NULL, *q = NULL;
     5         kx 
     5         kx           p = q = buf = xstrdup( (const char *)pkglog );
     5         kx           ++p;
     5         kx           while( *p != '\0' && !isblank(*p) && !(*q == '-' && isdigit(*p)) )
     5         kx           {
     5         kx             /* package version starts with a number and separated by '-' */
     5         kx             ++p; ++q;
     5         kx           }
     5         kx           *(--p) = '\0';
     5         kx 
     5         kx           /*******************************************************
     5         kx             We have to make sure that the name we are looking for
     5         kx             is not shorter than the name of the found package.
     5         kx            */
     5         kx           if( strlen(pkg_fname) >= strlen(buf) )
     5         kx           {
     5         kx 
     5         kx             pkg_found = xstrdup( (const char *)path );
     5         kx             free( buf );
     5         kx             closedir( dir );
     5         kx             return;
     5         kx           }
     5         kx           free( buf );
     5         kx         }
     5         kx       }
     5         kx       if( S_ISDIR(entry_sb.st_mode) && grp == NULL )
     5         kx       {
     5         kx         _probe_pkglog( (const char *)path, (const char *)entry->d_name );
     5         kx       }
     5         kx     }
     5         kx     /* else { stat() returns error code; errno is set; and we have to continue the loop } */
     5         kx   }
     5         kx 
     5         kx   closedir( dir );
     5         kx }
     5         kx 
     5         kx /***********************************************************
     5         kx   probe_package():
     5         kx   ---------------
     5         kx  */
     5         kx char *probe_package( void )
     5         kx {
     5         kx   char *ret = NULL;
     5         kx 
     5         kx   _probe_pkglog( (const char *)pkgs_path, NULL );
     5         kx   if( pkg_found )
     5         kx   {
     5         kx     free( pkg_fname );
     5         kx     ret = pkg_fname = pkg_found;
     5         kx   }
     5         kx 
     5         kx   return ret;
     5         kx }
     5         kx /*
     5         kx   Enf of Probe functions.
     5         kx  ***********************************************************/
     5         kx 
     5         kx 
     5         kx 
     5         kx static void read_input_pkginfo( const char *pkginfo_fname )
     5         kx {
     5         kx   char *ln      = NULL;
     5         kx   char *line    = NULL;
     5         kx 
     5         kx   FILE *pkginfo = NULL;
     5         kx 
     5         kx   if( pkginfo_fname != NULL )
     5         kx   {
     5         kx     pkginfo = fopen( (const char *)pkginfo_fname, "r" );
     5         kx     if( !pkginfo )
     5         kx     {
     5         kx       FATAL_ERROR( "Cannot open %s file", pkginfo_fname );
     5         kx     }
     5         kx   }
     5         kx 
     5         kx   line = (char *)malloc( (size_t)PATH_MAX );
     5         kx   if( !line )
     5         kx   {
     5         kx     FATAL_ERROR( "Cannot allocate memory" );
     5         kx   }
     5         kx 
     5         kx   while( (ln = fgets( line, PATH_MAX, pkginfo )) )
     5         kx   {
     5         kx     char *match = NULL;
     5         kx 
     5         kx     ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol      */
     5         kx     skip_eol_spaces( ln );     /* remove spaces at end-of-line */
     5         kx 
     5         kx     if( (match = strstr( ln, "pkgname" )) && match == ln ) {
     5         kx       char *p = index( match, '=' ) + 1;
     5         kx       if( p != NULL ) pkgname = skip_spaces( p );
     5         kx     }
     5         kx     if( (match = strstr( ln, "pkgver" )) && match == ln ) {
     5         kx       char *p = index( match, '=' ) + 1;
     5         kx       if( p != NULL ) pkgver = skip_spaces( p );
     5         kx     }
     5         kx     if( (match = strstr( ln, "arch" )) && match == ln ) {
     5         kx       char *p = index( match, '=' ) + 1;
     5         kx       if( p != NULL ) arch = skip_spaces( p );
     5         kx     }
     5         kx     if( (match = strstr( ln, "distroname" )) && match == ln ) {
     5         kx       char *p = index( match, '=' ) + 1;
     5         kx       if( p != NULL ) distroname = skip_spaces( p );
     5         kx     }
     5         kx     if( (match = strstr( ln, "distrover" )) && match == ln ) {
     5         kx       char *p = index( match, '=' ) + 1;
     5         kx       if( p != NULL ) distrover = skip_spaces( p );
     5         kx     }
     5         kx 
     5         kx     if( (match = strstr( ln, "group" )) && match == ln ) {
     5         kx       char *p = index( match, '=' ) + 1;
     5         kx       if( p != NULL ) group = skip_spaces( p );
     5         kx     }
     5         kx   }
     5         kx 
     5         kx   free( line );
     5         kx 
     5         kx   if( !pkgname || !pkgver || !arch || !distroname || !distrover )
     5         kx   {
     5         kx     FATAL_ERROR( "Invalid input .PKGINFO file" );
     5         kx   }
     5         kx 
     5         kx   fclose( pkginfo );
     5         kx }
     5         kx 
     5         kx static void read_found_pkginfo( const char *pkginfo_fname )
     5         kx {
     5         kx   char *ln      = NULL;
     5         kx   char *line    = NULL;
     5         kx 
     5         kx   FILE *pkginfo = NULL;
     5         kx 
     5         kx   char *pn = NULL, *pv = NULL, *ar = NULL, *dn = NULL, *dv = NULL;
     5         kx 
     5         kx   if( pkginfo_fname != NULL )
     5         kx   {
     5         kx     pkginfo = fopen( (const char *)pkginfo_fname, "r" );
     5         kx     if( !pkginfo )
     5         kx     {
     5         kx       FATAL_ERROR( "Cannot open %s file", pkginfo_fname );
     5         kx     }
     5         kx   }
     5         kx 
     5         kx   line = (char *)malloc( (size_t)PATH_MAX );
     5         kx   if( !line )
     5         kx   {
     5         kx     FATAL_ERROR( "Cannot allocate memory" );
     5         kx   }
     5         kx 
     5         kx   while( (ln = fgets( line, PATH_MAX, pkginfo )) )
     5         kx   {
     5         kx     char *match = NULL;
     5         kx 
     5         kx     ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol      */
     5         kx     skip_eol_spaces( ln );     /* remove spaces at end-of-line */
     5         kx 
     5         kx     if( (match = strstr( ln, "pkgname" )) && match == ln ) {
     5         kx       char *p = index( match, '=' ) + 1;
     5         kx       if( p != NULL ) pn = skip_spaces( p );
     5         kx     }
     5         kx     if( (match = strstr( ln, "pkgver" )) && match == ln ) {
     5         kx       char *p = index( match, '=' ) + 1;
     5         kx       if( p != NULL ) pv = skip_spaces( p );
     5         kx     }
     5         kx     if( (match = strstr( ln, "arch" )) && match == ln ) {
     5         kx       char *p = index( match, '=' ) + 1;
     5         kx       if( p != NULL ) ar = skip_spaces( p );
     5         kx     }
     5         kx     if( (match = strstr( ln, "distroname" )) && match == ln ) {
     5         kx       char *p = index( match, '=' ) + 1;
     5         kx       if( p != NULL ) dn = skip_spaces( p );
     5         kx     }
     5         kx     if( (match = strstr( ln, "distrover" )) && match == ln ) {
     5         kx       char *p = index( match, '=' ) + 1;
     5         kx       if( p != NULL ) dv = skip_spaces( p );
     5         kx     }
     5         kx   }
     5         kx 
     5         kx   free( line );
     5         kx 
     5         kx   if( !pn || !pv || !ar || !dn || !dv )
     5         kx   {
     5         kx     FATAL_ERROR( "Invalid input .PKGINFO file" );
     5         kx   }
     5         kx   else
     5         kx   {
     5         kx     if( pn ) free( pn );
     5         kx     if( pv ) installed_version = pv;
     5         kx     if( ar ) free( ar );
     5         kx     if( dn ) free( dn );
     5         kx     if( dv ) free( dv );
     5         kx   }
     5         kx 
     5         kx   fclose( pkginfo );
     5         kx }
     5         kx 
     5         kx static void _get_found_pkginfo( const char *pkglog )
     5         kx {
     5         kx   pid_t p = (pid_t) -1;
     5         kx   int   rc;
     5         kx 
     5         kx   int   len = 0;
     5         kx   char *tmp= NULL, *cmd = NULL;
     5         kx 
     5         kx   tmp = (char *)malloc( (size_t)PATH_MAX );
     5         kx   if( !tmp ) { FATAL_ERROR( "Cannot allocate memory" ); }
     5         kx   bzero( (void *)tmp, PATH_MAX );
     5         kx 
     5         kx   (void)sprintf( &tmp[0], "%s", tmpdir );
     5         kx   if( _mkdir_p( tmp, S_IRWXU | S_IRWXG | S_IRWXO ) != 0 )
     5         kx   {
     5         kx     FATAL_ERROR( "Cannot get PKGINFO from '%s' file", basename( (char *)pkglog ) );
     5         kx   }
     5         kx 
     5         kx   cmd = (char *)malloc( (size_t)PATH_MAX );
     5         kx   if( !cmd ) { FATAL_ERROR( "Cannot allocate memory" ); }
     5         kx   bzero( (void *)cmd, PATH_MAX );
     5         kx 
     5         kx   len = snprintf( &cmd[0], PATH_MAX,
     5         kx                   "%s/pkginfo -d %s -o pkginfo,restore-links,filelist %s > /dev/null 2>&1",
     5         kx                   selfdir, tmp, pkglog );
     5         kx   if( len == 0 || len == PATH_MAX - 1 )
     5         kx   {
     5         kx     FATAL_ERROR( "Cannot get PKGINFO from %s file", basename( (char *)pkglog ) );
     5         kx   }
     5         kx   p = sys_exec_command( cmd );
     5         kx   rc = sys_wait_command( p, (char *)NULL, PATH_MAX );
     5         kx   if( rc != 0 )
     5         kx   {
     5         kx     FATAL_ERROR( "Cannot get PKGINFO from '%s' file", basename( (char *)pkglog ) );
     5         kx   }
     5         kx 
     5         kx   (void)strcat( tmp, "/.PKGINFO" );
     5         kx   read_found_pkginfo( (const char *)&tmp[0] );
     5         kx   *(strstr( tmp, "/.PKGINFO" )) = '\0'; /* :restore tmpdir in tmp[] buffer */
     5         kx 
     5         kx   free( tmp );
     5         kx   free( cmd );
     5         kx }
     5         kx 
     5         kx static void _search_pkglog( const char *dirpath, const char *grp )
     5         kx {
     5         kx   DIR    *dir;
     5         kx   char   *path;
     5         kx   size_t  len;
     5         kx 
     5         kx   struct stat    path_sb, entry_sb;
     5         kx   struct dirent *entry;
     5         kx 
     5         kx   char   *pname = (char *)dirpath + strlen( root ); /* do not remove leading '/' */
     5         kx 
     5         kx   if( stat( dirpath, &path_sb ) == -1 )
     5         kx   {
     5         kx     FATAL_ERROR( "%s: Cannot stat Setup Database or group directory", pname );
     5         kx   }
     5         kx 
     5         kx   if( S_ISDIR(path_sb.st_mode) == 0 )
     5         kx   {
     5         kx     FATAL_ERROR( "%s: Setup Database or group is not a directory", pname );
     5         kx   }
     5         kx 
     5         kx   if( (dir = opendir(dirpath) ) == NULL )
     5         kx   {
     5         kx     FATAL_ERROR( "Canot access %s directory: %s", pname, strerror( errno ) );
     5         kx   }
     5         kx 
     5         kx   len = strlen( dirpath );
     5         kx 
     5         kx   while( (entry = readdir( dir )) != NULL)
     5         kx   {
     5         kx     /* skip entries '.' and '..' */
     5         kx     if( ! strcmp( entry->d_name, "." ) || ! strcmp( entry->d_name, ".." ) ) continue;
     5         kx 
     5         kx     /* determinate a full name of an entry */
     5         kx     path = alloca( len + strlen( entry->d_name ) + 2 );
     5         kx 
     5         kx     strcpy( path, dirpath );
     5         kx     strcat( path, "/" );
     5         kx     strcat( path, entry->d_name );
     5         kx 
     5         kx     if( stat( path, &entry_sb ) == 0 )
     5         kx     {
     5         kx       if( S_ISREG(entry_sb.st_mode) )
     5         kx       {
     5         kx         char *match = NULL, *name  = basename( path );
     5         kx 
     5         kx         if( (match = strstr( name, pkgname )) && match == name )
     5         kx         {
     5         kx           /****************************************************************
     5         kx             Здесь мы еще должны проверить, что найденный пакет не имеет
     5         kx             более длинное имя, которое начинается с имени искомого пакета.
     5         kx             Полагаясь на факт, что версия может начинаться только с цифры,
     5         kx             мы пропускаем символ '-', разделяющий имя и версию пакета,
     5         kx             а затем проверяем начальный символ версии:
     5         kx            */
     5         kx           if( *(name + strlen( pkgname )) == '-' && isdigit( *(name + strlen( pkgname ) + 1) ) )
     5         kx           {
     5         kx             _get_found_pkginfo( (const char *)path );
     5         kx           }
     5         kx         }
     5         kx       }
     5         kx       if( S_ISDIR(entry_sb.st_mode) && grp == NULL )
     5         kx       {
     5         kx         /**************************************************************************
     5         kx           NOTE:
     5         kx             In the Setup Database can be only one package with the same pkgname
     5         kx             but in different groups. For example, the package named 'cairo'
     5         kx             has two instance: libs/cairo-1.14.6 and xlibs/cairo-1.14.6. During
     5         kx             system installation the package libs/cairo-1.14.6 installed first
     5         kx             and then updated by xlibs/cairo-1.14.6 and PKGLOG of libs/cairo-1.14.6
     5         kx             moved from /var/log/radix/packages to /var/log/radix/removed-packages.
     5         kx 
     5         kx             So here we have to look for the PKGLOG in all group directories:
     5         kx          */
     5         kx         _search_pkglog( (const char *)path, (const char *)entry->d_name );
     5         kx       }
     5         kx     }
     5         kx     /* else { stat() returns error code; errno is set; and we have to continue the loop } */
     5         kx   }
     5         kx 
     5         kx   closedir( dir );
     5         kx }
     5         kx 
     5         kx static void find_installed_package( void )
     5         kx {
     5         kx   char *tmp = NULL;
     5         kx 
     5         kx   tmp = (char *)malloc( (size_t)PATH_MAX );
     5         kx   if( !tmp ) { FATAL_ERROR( "Cannot allocate memory" ); }
     5         kx   bzero( (void *)tmp, PATH_MAX );
     5         kx 
     5         kx   (void)sprintf( &tmp[0], "%s", pkgs_path );
     5         kx 
     5         kx   _search_pkglog( (const char *)&tmp[0], NULL );
     5         kx 
     5         kx   free( tmp );
     5         kx }
     5         kx 
     5         kx /***************************************************************
     5         kx   check_input_package():
     5         kx   ---------------------
     5         kx 
     5         kx     Возвращает:
     5         kx      -1 если пакет установлен, но его версия меньше
     5         kx         запрашиваемого,
     5         kx       0 если пакет не установлен или версия установленного и
     5         kx         запрашиваемого равны,
     5         kx       1 если пакет установлен, но его версия больше
     5         kx         запрашиваемого.
     5         kx 
     5         kx     В случае возврата -1 или 1, устанавливается переменная
     5         kx     installed_version, равная версии уже установленного пакета.
     5         kx 
     5         kx     В случае возврата нуля есть два варанта:
     5         kx       а) пакет установлен и его надо проверить на целостность
     5         kx          (переменная installed_version != NULL );
     5         kx       б) пакет не установлен.
     5         kx 
     5         kx     Если installed_version != NULL, проверка целостности
     5         kx     пакета будет осуществлена, вне зависимости от его версии.
     5         kx  */
     5         kx static int check_input_package( void )
     5         kx {
     5         kx   struct stat st;
     5         kx   char *fname = pkg_fname;
     5         kx 
     5         kx   enum _input_type  type = IFMT_UNKNOWN;
     5         kx   char              uncompress = '\0';
     5         kx 
     5         kx   int ret = 0;
     5         kx 
     5         kx   bzero( (void *)&st, sizeof( struct stat ) );
     5         kx 
     5         kx   if( stat( (const char *)fname, &st ) == -1 )
     5         kx   {
     5         kx     /*************************************************
     5         kx       Specified pkg_fname is not a file or directory.
     5         kx       Try to find installed package  with name equal
     5         kx       to pkg_fname:
     5         kx      */
     5         kx     fname = NULL;
     5         kx     fname = probe_package();
     5         kx     if( !fname )
     5         kx     {
     5         kx       if( !quiet ) fprintf( stdout, "Specified package '%s' is not installed.\n\n", pkg_fname );
     5         kx 
     5         kx       exit_status = 30; /* Package is not installed: install */
     5         kx 
     5         kx       if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); }
     5         kx       free_resources();
     5         kx 
     5         kx       exit( exit_status );
     5         kx     }
     5         kx   }
     5         kx 
     5         kx   /* check pkg_fname again: */
     5         kx   if( stat( (const char *)fname, &st ) == -1 )
     5         kx   {
     5         kx     FATAL_ERROR( "Cannot access input '%s' file: %s", fname, strerror( errno ) );
     5         kx   }
     5         kx 
     5         kx   type = check_input_file( &uncompress, fname );
     5         kx   if( type == IFMT_UNKNOWN )
     5         kx   {
     5         kx     FATAL_ERROR( "Unknown format of input '%s' file", fname );
     5         kx   }
     5         kx 
     5         kx   if( S_ISREG(st.st_mode) )
     5         kx   {
     5         kx     pid_t p = (pid_t) -1;
     5         kx     int   rc;
     5         kx 
     5         kx     int   len = 0;
     5         kx     char *tmp= NULL, *cmd = NULL;
     5         kx 
     5         kx     tmp = (char *)malloc( (size_t)PATH_MAX );
     5         kx     if( !tmp ) { FATAL_ERROR( "Cannot allocate memory" ); }
     5         kx     bzero( (void *)tmp, PATH_MAX );
     5         kx 
     5         kx     (void)sprintf( &tmp[0], "%s", tmpdir );
     5         kx     if( _mkdir_p( tmp, S_IRWXU | S_IRWXG | S_IRWXO ) != 0 )
     5         kx     {
     5         kx       FATAL_ERROR( "Cannot get PKGINFO from '%s' file", basename( (char *)fname ) );
     5         kx     }
     5         kx 
     5         kx     cmd = (char *)malloc( (size_t)PATH_MAX );
     5         kx     if( !cmd ) { FATAL_ERROR( "Cannot allocate memory" ); }
     5         kx     bzero( (void *)cmd, PATH_MAX );
     5         kx 
     5         kx     len = snprintf( &cmd[0], PATH_MAX,
     5         kx                     "%s/pkginfo -d %s -o pkginfo,restore-links,filelist %s > /dev/null 2>&1",
     5         kx                     selfdir, tmp, fname );
     5         kx     if( len == 0 || len == PATH_MAX - 1 )
     5         kx     {
     5         kx       FATAL_ERROR( "Cannot get PKGINFO from %s file", basename( (char *)fname ) );
     5         kx     }
     5         kx     p = sys_exec_command( cmd );
     5         kx     rc = sys_wait_command( p, (char *)NULL, PATH_MAX );
     5         kx     if( rc != 0 )
     5         kx     {
     5         kx       FATAL_ERROR( "Cannot get PKGINFO from '%s' file", basename( (char *)fname ) );
     5         kx     }
     5         kx 
     5         kx     (void)strcat( tmp, "/.PKGINFO" );
     5         kx     read_input_pkginfo( (const char *)&tmp[0] );
     5         kx     *(strstr( tmp, "/.PKGINFO" )) = '\0'; /* :restore tmpdir in tmp[] buffer */
     5         kx 
     5         kx     find_installed_package();
     5         kx 
     5         kx     free( cmd );
     5         kx     free( tmp );
     5         kx 
     5         kx     if( installed_version )
     5         kx     {
     5         kx       ret = cmp_version( (const char *)installed_version, (const char *)pkgver );
     5         kx     }
     5         kx   }
     5         kx   else
     5         kx   {
     5         kx     FATAL_ERROR( "Input %s file is not a regular file", basename( (char *)fname ) );
     5         kx   }
     5         kx 
     5         kx   return ret;
     5         kx }
     5         kx 
     5         kx static int check_package_integrity( void )
     5         kx {
     5         kx   struct stat st;
     5         kx   FILE  *fp = NULL;
     5         kx 
     5         kx   char *ln   = NULL;
     5         kx   char *line = NULL;
     5         kx 
     5         kx   char *buf = NULL, *tmp = NULL;
     5         kx 
     5         kx   int restore_links = 0;
     5         kx   int ret = 1;
     5         kx 
     5         kx   buf = (char *)malloc( (size_t)PATH_MAX );
     5         kx   if( !buf ) { FATAL_ERROR( "Cannot allocate memory" ); }
     5         kx   bzero( (void *)buf, PATH_MAX );
     5         kx 
     5         kx   tmp = (char *)malloc( (size_t)PATH_MAX );
     5         kx   if( !tmp ) { FATAL_ERROR( "Cannot allocate memory" ); }
     5         kx   bzero( (void *)tmp, PATH_MAX );
     5         kx 
     5         kx   /* Check if .RESTORELINKS is present */
     5         kx   (void)sprintf( &tmp[0], "%s/.RESTORELINKS", tmpdir );
     5         kx   bzero( (void *)&st, sizeof( struct stat ) );
     5         kx   if( (stat( (const char *)&tmp[0], &st ) == 0) && (st.st_size > 8) )
     5         kx   {
     5         kx     restore_links = 1;
     5         kx   }
     5         kx 
     5         kx   (void)sprintf( &tmp[0], "%s/.FILELIST", tmpdir );
     5         kx   fp = fopen( (const char *)&tmp[0], "r" );
     5         kx   if( !fp )
     5         kx   {
     5         kx     FATAL_ERROR( "Cannot open .FILELIST file" );
     5         kx   }
     5         kx 
     5         kx   line = (char *)malloc( (size_t)PATH_MAX );
     5         kx   if( !line ) { FATAL_ERROR( "Cannot allocate memory" ); }
     5         kx   bzero( (void *)line, PATH_MAX );
     5         kx 
     5         kx   while( (ln = fgets( line, PATH_MAX, fp )) )
     5         kx   {
     5         kx     int dir = 0;
     5         kx 
     5         kx     ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol      */
     5         kx     skip_eol_spaces( ln );     /* remove spaces at end-of-line */
     5         kx 
     5         kx     if( *(ln + strlen(ln) - 1) == '/' ) { dir = 1; *(ln + strlen(ln) - 1) = '\0'; }
     5         kx     else                                { dir = 0; }
     5         kx 
     5         kx     if( !dir )
     5         kx     {
     5         kx       char *p = rindex( ln, '.' );
     5         kx       if( p && !strncmp( (const char *)p, ".new", 4 ) )
     5         kx       {
     5         kx         /**************************
     5         kx           Do not check .new files:
     5         kx          */
     5         kx         *p = '\0';
     5         kx       }
     5         kx     }
     5         kx 
     5         kx     (void)sprintf( &buf[0], "%s/%s", root, ln );
     5         kx     bzero( (void *)&st, sizeof( struct stat ) );
     5         kx 
     5         kx     if( lstat( (const char *)&buf[0], &st ) == -1 )
     5         kx     {
     5         kx       /* cannot access file list entry */
     5         kx       if( dir )
     5         kx       {
     5         kx         if( print_broken_files )
     5         kx           fprintf( stdout, "%s-%s: /%s: no such directory\n", pkgname, installed_version, ln );
     5         kx       }
     5         kx       else
     5         kx       {
     5         kx         if( print_broken_files )
     5         kx           fprintf( stdout, "%s-%s: /%s: no such file\n", pkgname, installed_version, ln );
     5         kx       }
     5         kx 
     5         kx       ret = 0; continue;
     5         kx     }
     5         kx 
     5         kx     if( dir )
     5         kx     {
     5         kx       if( S_ISDIR(st.st_mode) == 0 )
     5         kx       {
     5         kx         /* not a directory */
     5         kx         if( print_broken_files )
     5         kx           fprintf( stdout, "%s-%s: /%s: not a directory\n", pkgname, installed_version, ln );
     5         kx         ret = 0; continue;
     5         kx       }
     5         kx     }
     5         kx     else
     5         kx     {
     5         kx       if( S_ISREG(st.st_mode) == 0 )
     5         kx       {
     5         kx         /* not a regular file */
     5         kx         if( print_broken_files )
     5         kx           fprintf( stdout, "%s-%s: /%s: not a regular file\n", pkgname, installed_version, ln );
     5         kx         ret = 0; continue;
     5         kx       }
     5         kx       if( !restore_links )
     5         kx       {
     5         kx         if( S_ISLNK(st.st_mode) == 0 )
     5         kx         {
     5         kx           /* not a symbolic link */
     5         kx           if( print_broken_files )
     5         kx             fprintf( stdout, "%s-%s: /%s: not a symbolic link\n", pkgname, installed_version, ln );
     5         kx           ret = 0; continue;
     5         kx         }
     5         kx       }
     5         kx     }
     5         kx   } /* End of while( file list entry ) */
     5         kx   fclose( fp );
     5         kx 
     5         kx 
     5         kx   (void)sprintf( &tmp[0], "%s/.RESTORELINKS", tmpdir );
     5         kx   bzero( (void *)&st, sizeof( struct stat ) );
     5         kx 
     5         kx   if( stat( (const char *)&tmp[0], &st ) == 0 )
     5         kx   {
     5         kx     fp = fopen( (const char *)&tmp[0], "r" );
     5         kx     if( !fp )
     5         kx     {
     5         kx       FATAL_ERROR( "Cannot open .RESTORELINKS file" );
     5         kx     }
     5         kx 
     5         kx     while( (ln = fgets( line, PATH_MAX, fp )) )
     5         kx     {
     5         kx       char *match = NULL;
     5         kx 
     5         kx       ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol      */
     5         kx       skip_eol_spaces( ln );     /* remove spaces at end-of-line */
     5         kx 
     5         kx       if( (match = strstr( ln, "; rm -rf " )) )
     5         kx       {
     5         kx         char *q = NULL;
     5         kx         char *p = strstr( ln, "cd" ) + 2;
     5         kx         char *f = strstr( ln, "; rm -rf" ) + 8;
     5         kx 
     5         kx         if( !p || !f ) continue;
     5         kx 
     5         kx         while( (*p == ' ' || *p == '\t') && *p != '\0' ) ++p;
     5         kx         while( (*f == ' ' || *f == '\t') && *f != '\0' ) ++f;
     5         kx 
     5         kx         q = p; while( *q != ' ' && *q != '\t' && *q != ';' && *q != '\0' ) ++q; *q = '\0';
     5         kx         q = f; while( *q != ' ' && *q != '\t' && *q != ';' && *q != '\0' ) ++q; *q = '\0';
     5         kx 
     5         kx         if( p && f )
     5         kx         {
     5         kx           (void)sprintf( &buf[0], "%s/%s/%s", root, p, f );
     5         kx           bzero( (void *)&st, sizeof( struct stat ) );
     5         kx 
     5         kx           if( lstat( (const char *)&buf[0], &st ) == -1 )
     5         kx           {
     5         kx             /* cannot access restore links entry */
     5         kx             if( print_broken_files )
     5         kx               fprintf( stdout, "%s-%s: /%s/%s: no such file or directory\n", pkgname, installed_version, p, f );
     5         kx             ret = 0; continue;
     5         kx           }
     5         kx 
     5         kx           if( S_ISLNK(st.st_mode) == 0 )
     5         kx           {
     5         kx             /* not a symbolic link */
     5         kx             if( print_broken_files )
     5         kx               fprintf( stdout, "%s-%s: /%s/%s: not a symbolic link\n", pkgname, installed_version, p, f );
     5         kx             ret = 0; continue;
     5         kx           }
     5         kx         }
     5         kx       }
     5         kx     } /* End of while( restore links entry ) */
     5         kx     fclose( fp );
     5         kx   }
     5         kx 
     5         kx   free( line );
     5         kx   free( tmp );
     5         kx   free( buf );
     5         kx 
     5         kx   return ret;
     5         kx }
     5         kx 
     5         kx 
     5         kx /*********************************************
     5         kx   Get directory where this program is placed:
     5         kx  */
     5         kx char *get_selfdir( void )
     5         kx {
     5         kx   char    *buf = NULL;
     5         kx   ssize_t  len;
     5         kx 
     5         kx   buf = (char *)malloc( PATH_MAX );
     5         kx   if( !buf )
     5         kx   {
     5         kx     FATAL_ERROR( "Cannot allocate memory" );
     5         kx   }
     5         kx 
     5         kx   bzero( (void *)buf, PATH_MAX );
     5         kx   len = readlink( "/proc/self/exe", buf, (size_t)PATH_MAX );
     5         kx   if( len > 0 && len < PATH_MAX )
     5         kx   {
     5         kx     char *p = xstrdup( (const char *)dirname( buf ) );
     5         kx     free( buf );
     5         kx     return p;
     5         kx   }
     5         kx   FATAL_ERROR( "Cannot determine self directory. Please mount /proc filesystem" );
     5         kx }
     5         kx 
     5         kx void set_stack_size( void )
     5         kx {
     5         kx   const rlim_t   stack_size = 16 * 1024 * 1024; /* min stack size = 16 MB */
     5         kx   struct rlimit  rl;
     5         kx   int ret;
     5         kx 
     5         kx   ret = getrlimit( RLIMIT_STACK, &rl );
     5         kx   if( ret == 0 )
     5         kx   {
     5         kx     if( rl.rlim_cur < stack_size )
     5         kx     {
     5         kx       rl.rlim_cur = stack_size;
     5         kx       ret = setrlimit( RLIMIT_STACK, &rl );
     5         kx       if( ret != 0 )
     5         kx       {
     5         kx         fprintf(stderr, "setrlimit returned result = %d\n", ret);
     5         kx         FATAL_ERROR( "Cannot set stack size" );
     5         kx       }
     5         kx     }
     5         kx   }
     5         kx }
     5         kx 
     5         kx 
     5         kx int main( int argc, char *argv[] )
     5         kx {
     5         kx   gid_t  gid;
     5         kx 
     5         kx   set_signal_handlers();
     5         kx 
     5         kx   gid = getgid();
     5         kx   setgroups( 1, &gid );
     5         kx 
     5         kx   fatal_error_hook = fatal_error_actions;
     5         kx 
     5         kx   selfdir = get_selfdir();
     5         kx 
     5         kx   errlog = stderr;
     5         kx 
     5         kx   program = basename( argv[0] );
     5         kx   get_args( argc, argv );
     5         kx 
     5         kx   /* set_stack_size(); */
     5         kx 
     5         kx   tmpdir = _mk_tmpdir();
     5         kx   if( !tmpdir )
     5         kx   {
     5         kx     FATAL_ERROR( "Cannot create temporary directory" );
     5         kx   }
     5         kx 
     5         kx 
     5         kx   {
     5         kx     int status = 0, correctly = 0;
     5         kx 
     5         kx     /**********************************************************
     5         kx       Fill pkginfo data and put or replace pkglog into tmpdir:
     5         kx      */
     5         kx     status = check_input_package();
     5         kx 
     5         kx     if( installed_version )
     5         kx     {
     5         kx       /* In this case we have to check package integrity */
     5         kx       correctly = check_package_integrity(); /* returns 1 if correct */
     5         kx     }
     5         kx 
     5         kx     if( exit_status == EXIT_SUCCESS )
     5         kx     {
     5         kx       if( status < 0 )
     5         kx       {
     5         kx         if( !correctly )
     5         kx         {
     5         kx           if( !quiet )
     5         kx             fprintf( stdout,
     5         kx                      "Previous version '%s' of specified package '%s-%s' is installed but not correct.\n\n",
     5         kx                      installed_version, pkgname, pkgver );
     5         kx           exit_status = 34;   /* Package is installed but not correct: repair, upgrade    */
     5         kx         }
     5         kx         else
     5         kx         {
     5         kx           if( !quiet )
     5         kx             fprintf( stdout,
     5         kx                      "Previous version '%s' of specified package '%s-%s' is installed.\n\n",
     5         kx                      installed_version, pkgname, pkgver );
     5         kx           exit_status = 33;   /* Package is installed correctly: upgrade                  */
     5         kx         }
     5         kx       }
     5         kx       else if( status > 0 )
     5         kx       {
     5         kx         if( !correctly )
     5         kx         {
     5         kx           if( !quiet )
     5         kx             fprintf( stdout,
     5         kx                      "A newer version '%s' of specified package '%s-%s' is installed but not correct.\n\n",
     5         kx                      installed_version, pkgname, pkgver );
     5         kx           exit_status = 36;   /* Package is installed but not correct: repair, downgrade  */
     5         kx         }
     5         kx         else
     5         kx         {
     5         kx           if( !quiet )
     5         kx             fprintf( stdout,
     5         kx                      "A newer version '%s' of specified package '%s-%s' is installed.\n\n",
     5         kx                      installed_version, pkgname, pkgver );
     5         kx           exit_status = 35;   /* Package is installed correctly: downgrade                */
     5         kx         }
     5         kx       }
     5         kx       else
     5         kx       {
     5         kx         if( installed_version )
     5         kx         {
     5         kx           if( !correctly )
     5         kx           {
     5         kx             if( !quiet )
     5         kx               fprintf( stdout,
     5         kx                        "Specified package '%s-%s' is already installed but not correct.\n\n",
     5         kx                        pkgname, pkgver );
     5         kx             exit_status = 32; /* Package is installed but not correct: repair, re-install */
     5         kx           }
     5         kx           else
     5         kx           {
     5         kx             if( !quiet )
     5         kx               fprintf( stdout,
     5         kx                        "Specified package '%s-%s' is already installed.\n\n",
     5         kx                        pkgname, pkgver );
     5         kx             exit_status = 31; /* Package is installed correctly: nothing to do            */
     5         kx           }
     5         kx         }
     5         kx         else
     5         kx         {
     5         kx           if( !quiet )
     5         kx             fprintf( stdout,
     5         kx                      "Specified package '%s-%s' is not installed.\n\n",
     5         kx                      pkgname, pkgver );
     5         kx           exit_status = 30;   /* Package is not installed: install */
     5         kx         }
     5         kx       }
     5         kx     }
     5         kx 
     5         kx   }
     5         kx 
     5         kx 
     5         kx   if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); }
     5         kx   free_resources();
     5         kx 
     5         kx   exit( exit_status );
     5         kx }