diff options
author | kx <kx@radix.pro> | 2023-04-11 01:18:34 +0300 |
---|---|---|
committer | kx <kx@radix.pro> | 2023-04-11 01:18:34 +0300 |
commit | 11c606a6888dc269ef018359469a7276c3ad8f67 (patch) | |
tree | 368294bb7cadcd5c44ccd082187d6a4433401027 /src/remove-package.c | |
parent | 8c55752ed5b29a22fdab9faaa6ff27b7cafa6791 (diff) | |
download | pkgtools-11c606a6888dc269ef018359469a7276c3ad8f67.tar.xz |
Version 0.2.1pkgtools-0.2.1
Diffstat (limited to 'src/remove-package.c')
-rw-r--r-- | src/remove-package.c | 2234 |
1 files changed, 2234 insertions, 0 deletions
diff --git a/src/remove-package.c b/src/remove-package.c new file mode 100644 index 0000000..ca83621 --- /dev/null +++ b/src/remove-package.c @@ -0,0 +1,2234 @@ + +/********************************************************************** + + 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/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 <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 <config.h> + +#include <msglog.h> +#include <system.h> + +#include <cmpvers.h> +#include <dlist.h> + +#if defined( HAVE_DIALOG ) +#include <dialog-ui.h> +#endif + +#define PROGRAM_NAME "remove-package" + +#include <defs.h> + + +char *program = PROGRAM_NAME; +char *root = NULL, *pkgs_path = NULL, *rempkgs_path = NULL, + *pkg_fname = NULL, *pkglog_fname = NULL, + *tmpdir = NULL, *rtmpdir = NULL, *curdir = NULL, *log_fname = NULL; + +int quiet = 0, ignore_chrefs_errors = 0; +char *description = NULL; + +int exit_status = EXIT_SUCCESS; /* errors counter */ +char *selfdir = NULL; + +static char *pkgname = NULL, + *pkgver = NULL, + *arch = NULL, + *distroname = NULL, + *distrover = NULL, + *group = NULL, + *short_description = NULL, + *url = NULL, + *license = NULL, + *uncompressed_size = NULL, + *compressed_size = NULL, + *total_files = NULL; + +static char *requested_version = NULL; + +enum _remove_mode { + CONSOLE = 0, + INFODIALOG, + MENUDIALOG +} remove_mode = CONSOLE; + +enum _input_type { + IFMT_PKG = 0, + IFMT_LOG, + + IFMT_UNKNOWN +} input_format = IFMT_PKG; + +char uncompress = '\0'; + +static struct dlist *dirs = NULL; +static struct dlist *files = NULL; +static struct dlist *links = NULL; + +static void free_list( struct dlist *list ); + + +#define FREE_PKGINFO_VARIABLES() \ + if( pkgname ) { free( pkgname ); } pkgname = NULL; \ + if( pkgver ) { free( pkgver ); } pkgver = NULL; \ + if( arch ) { free( arch ); } arch = NULL; \ + if( distroname ) { free( distroname ); } distroname = NULL; \ + if( distrover ) { free( distrover ); } distrover = NULL; \ + if( group ) { free( group ); } group = NULL; \ + if( short_description ) { free( short_description ); } short_description = NULL; \ + if( description ) { free( description ); } description = NULL; \ + if( url ) { free( url ); } url = NULL; \ + if( license ) { free( license ); } license = NULL; \ + if( uncompressed_size ) { free( uncompressed_size ); } uncompressed_size = NULL; \ + if( compressed_size ) { free( compressed_size ); } compressed_size = NULL; \ + if( total_files ) { free( total_files ); } total_files = NULL; \ + if( requested_version ) { free( requested_version ); } requested_version = NULL + +void free_resources() +{ + if( root ) { free( root ); root = NULL; } + if( pkgs_path ) { free( pkgs_path ); pkgs_path = NULL; } + if( rempkgs_path ) { free( rempkgs_path ); rempkgs_path = NULL; } + if( pkg_fname ) { free( pkg_fname ); pkg_fname = NULL; } + + if( dirs ) { free_list( dirs ); dirs = NULL; } + if( files ) { free_list( files ); files = NULL; } + if( links ) { free_list( links ); links = NULL; } + + if( rtmpdir ) { free( rtmpdir ); rtmpdir = NULL; } + if( curdir ) { free( curdir ); curdir = NULL; } + if( log_fname ) { free( log_fname ); log_fname = NULL; } + + if( selfdir ) { free( selfdir ); selfdir = NULL; } + + FREE_PKGINFO_VARIABLES(); +} + +void usage() +{ + free_resources(); + + fprintf( stdout, "\n" ); + fprintf( stdout, "Usage: %s [options] <package|pkglog|pkgname>\n", program ); + fprintf( stdout, "\n" ); + fprintf( stdout, "Remove installed package.\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, " --ignore-chrefs-errors Ignore change references errors (code: 48).\n" ); +#if defined( HAVE_DIALOG ) + fprintf( stdout, " -i,--info-dialog Show package description during remove\n" ); + fprintf( stdout, " process using ncurses dialog.\n" ); + fprintf( stdout, " -m,--menu-dialog Ask for confirmation the removal.\n" ); +#endif + fprintf( stdout, " -q,--quiet Do not display results. This option\n" ); + fprintf( stdout, " works unless options -i, -m\n" ); + fprintf( stdout, " are enabled.\n" ); + fprintf( stdout, " -r,--root=<DIR> Target rootfs path.\n" ); + fprintf( stdout, "\n" ); + fprintf( stdout, "Parameter:\n" ); + fprintf( stdout, " <package|pkglog|pkgname> The PKGNAME, PACKAGE tarball or PKGLOG.\n" ); + fprintf( stdout, "\n" ); + fprintf( stdout, "Return codes:\n" ); + fprintf( stdout, " ------+-------------------------------------------------------\n" ); + fprintf( stdout, " code | status\n" ); + fprintf( stdout, " ------+-------------------------------------------------------\n" ); + fprintf( stdout, " 30 | package is not installed\n" ); + fprintf( stdout, " ----+----\n" ); + fprintf( stdout, " 47 | cannot backup PKGLOG file in the Setup Database\n" ); + fprintf( stdout, " 43 | pre-remove script returned error status\n" ); + fprintf( stdout, " 46 | post-remove script returned error status\n" ); + fprintf( stdout, " 48 | references cannot be updated in Setup Database\n" ); + fprintf( stdout, " ------+-------------------------------------------------------\n" ); + fprintf( stdout, "\n" ); + fprintf( stdout, "Upon successful completion zero is returned. Other non-zero return\n" ); + fprintf( stdout, "codes imply incorrect completion of the deinstallation.\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(); +} + +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 ); + + memset( &sa, 0, sizeof( sa ) ); /* ignore SIGPIPE */ + sa.sa_handler = SIG_IGN; + sa.sa_flags = 0; + sigaction( SIGPIPE, &sa, NULL ); + + /* System V fork+wait does not work if SIGCHLD is ignored */ + signal( SIGCHLD, SIG_DFL ); +} + + +static enum _input_type check_input_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 = "hvimqr:"; +#else + const char* short_options = "hvqr:"; +#endif + +#define IGNORE_CHREFS_ERRORS 872 + + const struct option long_options[] = + { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'v' }, + { "ignore-chrefs-errors", no_argument, NULL, IGNORE_CHREFS_ERRORS }, +#if defined( HAVE_DIALOG ) + { "info-dialog", no_argument, NULL, 'i' }, + { "menu-dialog", no_argument, NULL, 'm' }, +#endif + { "quiet", no_argument, NULL, 'q' }, + { "root", required_argument, NULL, 'r' }, + { 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; + } + +#if defined( HAVE_DIALOG ) + case 'i': + { + remove_mode = INFODIALOG; + break; + } + case 'm': + { + remove_mode = MENUDIALOG; + break; + } +#endif + case 'q': + { + quiet = 1; + break; + } + + case IGNORE_CHREFS_ERRORS: + { + ignore_chrefs_errors = 1; + 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 = strdup( (const char *)cwd ); + } + else + { + root = strdup( (const char *)optarg ); + } + remove_trailing_slash( root ); + } + else + /* option is present but without value */ + usage(); + break; + } + + case '?': default: + { + usage(); + break; + } + } + } + + + 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 ); + + /* absolute path to input package: */ + if( argv[optind][0] != '/' && curdir ) + (void)sprintf( buf, "%s/%s", curdir, (const char *)argv[optind] ); + else + (void)strcpy( buf, (const char *)argv[optind] ); + + pkg_fname = strdup( (const char *)&buf[0] ); + free( buf ); + } + else + { + usage(); + } + + + if( !pkgs_path ) + { + 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 = strdup( (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 = strdup( (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 ); + } + + len = strlen( (const char *)buf ); + + (void)strcat( buf, PACKAGES_PATH ); + if( _mkdir_p( buf, S_IRWXU | S_IRWXG | S_IRWXO ) != 0 ) + { + FATAL_ERROR( "Cannot access '/%s' directory", PACKAGES_PATH ); + } + pkgs_path = strdup( (const char *)&buf[0] ); + + /********************************************* + Create other directories of Setup Database: + */ + buf[len] = '\0'; + (void)strcat( buf, REMOVED_PKGS_PATH ); + if( _mkdir_p( buf, S_IRWXU | S_IRWXG | S_IRWXO ) != 0 ) + { + FATAL_ERROR( "Cannot access '/%s' directory", REMOVED_PKGS_PATH ); + } + rempkgs_path = strdup( (const char *)&buf[0] ); + + buf[len] = '\0'; + (void)strcat( buf, SETUP_PATH ); + if( _mkdir_p( buf, S_IRWXU | S_IRWXG | S_IRWXO ) != 0 ) + { + FATAL_ERROR( "Cannot access '/%s' directory", SETUP_PATH ); + } + + /********************************************* + Allocate memory for Setup LOG File name: + */ + buf[len] = '\0'; + (void)strcat( buf, LOG_PATH ); + (void)strcat( buf, SETUP_LOG_FILE ); + log_fname = strdup( (const char *)&buf[0] ); + + free( buf ); + + } /* End if( !pkgs_path ) */ +} + +static void setup_log( char *format, ... ) +{ + FILE *fp = NULL; + + time_t t = time( NULL ); + struct tm tm = *localtime(&t); + + va_list argp; + + if( ! format ) return; + + fp = fopen( (const char *)log_fname, "a" ); + if( !fp ) + { + FATAL_ERROR( "Cannot open /%s%s file", LOG_PATH, SETUP_LOG_FILE ); + } + + fprintf( fp, "[%04d-%02d-%02d %02d:%02d:%02d]: ", + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec ); + + va_start( argp, format ); + vfprintf( fp, format, argp ); + fprintf( fp, "\n" ); + + fflush( fp ); + fclose( fp ); +} + +/*********************************************************** + Remove leading spaces and take non-space characters only: + (Especialy for pkginfo lines) + */ +static char *skip_spaces( char *s ) +{ + char *q, *p = (char *)0; + + if( !s || *s == '\0' ) return p; + + p = s; + + while( (*p == ' ' || *p == '\t') && *p != '\0' ) { ++p; } q = p; + while( *q != ' ' && *q != '\t' && *q != '\0' ) { ++q; } *q = '\0'; + + if( *p == '\0' ) return (char *)0; + + return( strdup( p ) ); +} + + +/******************************* + 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'; } +} + + + +/******************************************************** + Read .FILELIST and .RESTORELINKS functions: + */ +static int __cmp_list_items( const void *a, const void *b ) +{ + if( a && b ) + return strcmp( (const char *)a, (const char *)b ); + else if( a ) + return 1; + else + return -1; +} + +static void __free_list( void *data, void *user_data ) +{ + if( data ) { free( data ); } +} + +static void free_list( struct dlist *list ) +{ + if( list ) { dlist_free( list, __free_list ); } +} + +//////////////////////////////////////////////////// +//static void __print_list( void *data, void *user_data ) +//{ +// int *counter = (int *)user_data; +// +// if( counter ) { fprintf( stdout, "item[%.5d]: %s\n", *counter, (char *)data ); ++(*counter); } +// else { fprintf( stdout, "item: %s\n", (char *)data ); } +//} +// +//static void print_list( struct dlist *list ) +//{ +// int cnt = 0; +// if( list ) { dlist_foreach( list, __print_list, (void *)&cnt ); } +//} +//////////////////////////////////////////////////// + +static void read_filelist( void ) +{ + struct stat st; + FILE *fp = NULL; + + char *ln = NULL; + char *line = NULL, *tmp = NULL; + + tmp = (char *)malloc( (size_t)PATH_MAX ); + if( !tmp ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)tmp, PATH_MAX ); + + (void)sprintf( &tmp[0], "%s/.FILELIST", rtmpdir ); + bzero( (void *)&st, sizeof( struct stat ) ); + if( (stat( (const char *)&tmp[0], &st ) == -1) ) + { + FATAL_ERROR( "Cannot get .FILELIST from '%s' file", basename( (char *)pkglog_fname ) ); + } + + fp = fopen( (const char *)&tmp[0], "r" ); + if( !fp ) + { + FATAL_ERROR( "Cannot open .FILELIST file" ); + } + + 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 )) ) + { + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + skip_eol_spaces( ln ); /* remove spaces at end-of-line */ + + if( *(ln + strlen(ln) - 1) == '/' ) + { + *(ln + strlen(ln) - 1) = '\0'; + (void)sprintf( &tmp[0], "%s%s", (const char *)root, (const char *)ln ); + dirs = dlist_append( dirs, strdup( (const char *)&tmp[0] ) ); + } + else + { + (void)sprintf( &tmp[0], "%s%s", (const char *)root, (const char *)ln ); + files = dlist_append( files, strdup( (const char *)&tmp[0] ) ); + } + + } /* End of while( file list entry ) */ + + fclose( fp ); + + free( line ); + free( tmp ); +} + +static void read_restorelinks( void ) +{ + struct stat st; + FILE *fp = NULL; + + char *ln = NULL; + char *line = NULL, *tmp = NULL; + + tmp = (char *)malloc( (size_t)PATH_MAX ); + if( !tmp ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)tmp, PATH_MAX ); + + (void)sprintf( &tmp[0], "%s/.RESTORELINKS", rtmpdir ); + bzero( (void *)&st, sizeof( struct stat ) ); + if( (stat( (const char *)&tmp[0], &st ) == -1) || (st.st_size < 8) ) + { + free( tmp ); + return; + } + + fp = fopen( (const char *)&tmp[0], "r" ); + if( !fp ) + { + FATAL_ERROR( "Cannot open .RESTORELINKS file" ); + } + + 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 *match = NULL; + + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + skip_eol_spaces( ln ); /* remove spaces at end-of-line */ + + if( (match = strstr( ln, "; rm -rf " )) ) + { + char *q = NULL; + char *p = strstr( ln, "cd" ) + 2; + char *f = strstr( ln, "; rm -rf" ) + 8; + + if( !p || !f ) continue; + + while( (*p == ' ' || *p == '\t') && *p != '\0' ) ++p; + while( (*f == ' ' || *f == '\t') && *f != '\0' ) ++f; + + q = p; while( *q != ' ' && *q != '\t' && *q != ';' && *q != '\0' ) ++q; *q = '\0'; + q = f; while( *q != ' ' && *q != '\t' && *q != ';' && *q != '\0' ) ++q; *q = '\0'; + + if( p && f ) + { + (void)sprintf( &tmp[0], "%s%s/%s", (const char *)root, p, f ); + links = dlist_append( links, strdup( (const char *)&tmp[0] ) ); + } + } + } /* End of while( restore links entry ) */ + + fclose( fp ); + + free( line ); + free( tmp ); +} +/* + End of read .FILELIST and .RESTORELINKS functions. + ********************************************************/ + +static void read_description( void ) +{ + struct stat st; + FILE *fp = NULL; + + char *buf = NULL, *tmp = NULL; + char *lp = NULL; + int n = 0; + + char *ln = NULL; + char *line = NULL; + + tmp = (char *)malloc( (size_t)PATH_MAX ); + if( !tmp ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)tmp, PATH_MAX ); + + buf = (char *)malloc( (size_t)PATH_MAX ); + if( !buf ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)buf, PATH_MAX ); + + (void)sprintf( &tmp[0], "%s/.DESCRIPTION", rtmpdir ); + bzero( (void *)&st, sizeof( struct stat ) ); + if( (stat( (const char *)&tmp[0], &st ) == -1) || (st.st_size < 8) ) + { + free( tmp ); + return; + } + + fp = fopen( (const char *)&tmp[0], "r" ); + if( !fp ) + { + FATAL_ERROR( "Cannot open .DESCRIPTION file" ); + } + + (void)sprintf( (char *)&buf[0], "%s:", pkgname ); + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)line, PATH_MAX ); + + lp = (char *)&tmp[0]; + bzero( (void *)tmp, PATH_MAX ); + (void)sprintf( (char *)&tmp[0], "\n" ); + ++lp; + + while( (ln = fgets( line, PATH_MAX, fp )) && n < DESCRIPTION_NUMBER_OF_LINES ) + { + char *match = NULL; + + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + + if( (match = strstr( (const char *)ln, (const char *)buf )) && match == ln ) /* at start of line only */ + { + int mlen = strlen( match ), plen = strlen( buf ); + int length = ( mlen > plen ) ? (mlen - plen - 1) : 0 ; + + if( length > DESCRIPTION_LENGTH_OF_LINE ) + { + /* WARNING( "Package DESCRIPTION contains lines with length greater than %d characters", DESCRIPTION_LENGTH_OF_LINE ); */ + match[plen + 1 + DESCRIPTION_LENGTH_OF_LINE] = '\0'; /* truncating description line */ + skip_eol_spaces( match ); /* remove spaces at end-of-line */ + } + + match += plen + 1; + if( match[0] != '\0' ) { (void)sprintf( lp, " %s\n", match ); lp += strlen( match ) + 2; } + else { (void)sprintf( lp, "\n" ); ++lp; } + ++n; + } + } /* End of while( ln = fgets() ) */ + + fclose( fp ); + + (void)sprintf( lp, " Uncompressed Size: %s\n", uncompressed_size ); + lp += strlen( uncompressed_size ) + 21; + + description = strdup( (const char *)&tmp[0] ); + + free( buf ); + free( line ); + free( tmp ); +} + +static void pre_remove_routine( void ) +{ + pid_t p = (pid_t) -1; + int rc; + + int len = 0; + char *cmd = NULL; + + 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, + "cd %s && %s/.INSTALL pre_remove %s > /dev/null 2>&1", + root, rtmpdir, pkgver ); + if( len == 0 || len == PATH_MAX - 1 ) + { + FATAL_ERROR( "Cannot run pre-remove script for '%s-%s' package", pkgname, pkgver ); + } + p = sys_exec_command( cmd ); + rc = sys_wait_command( p, (char *)NULL, PATH_MAX ); + + free( cmd ); + + if( rc != 0 ) + { + exit_status = 43; + + if( remove_mode != CONSOLE ) + { +#if defined( HAVE_DIALOG ) + info_pkg_box( "Remove:", pkgname, pkgver, NULL, + "\n\\Z1Pre-remove script returned error status.\\Zn\n", 5, 0, 0 ); +#else + fprintf( stdout, "\nPre-remove script of '%s-%s' returned error status.\n\n", pkgname, pkgver ); +#endif + } + else + { + fprintf( stdout, "\nPre-remove script of '%s-%s' returned error status.\n\n", pkgname, pkgver ); + } + + if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); } + free_resources(); + exit( exit_status ); + } +} + +/******************************************************** + Removal functions: + */ +static void __remove_link( void *data, void *user_data ) +{ + const char *fname = (const char *)data; + + if( fname ) + { + (void)unlink( fname ); + } +} + +static void __remove_file( void *data, void *user_data ) +{ + const char *fname = (const char *)data; + + if( fname ) + { + char *p = rindex( fname, '.' ); + /* + Если .new файл остался с тем же именем, это значит что до инсталляции + в системе существовал такой же файл но без расширения .new и при этом + он отличался от нового. В данном случае надо удалять только файл .new. + + Если же файл .new не существует, то надо удалять такой же файл но без + расширения .new . + */ + if( p && !strncmp( (const char *)p, ".new", 4 ) ) + { + struct stat st; + + bzero( (void *)&st, sizeof( struct stat ) ); + if( (stat( fname, &st ) == -1) ) *p = '\0'; + } + + (void)unlink( fname ); + } +} + +static int is_dir_empty( const char *dirpath ) +{ + int ret = 0; + + DIR *dir; + char *path; + size_t len; + + struct stat path_sb, entry_sb; + struct dirent *entry; + + if( stat( dirpath, &path_sb ) == -1 ) return ret; /* stat returns error code; errno is set */ + if( S_ISDIR(path_sb.st_mode) == 0 ) return ret; /* dirpath is not a directory */ + if( (dir = opendir(dirpath) ) == NULL ) return ret; /* Cannot open direcroty; errno is set */ + + ret = 1; + + 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 ) + { + ret = 0; + break; + } + /* else { stat() returns error code; errno is set; and we have to continue the loop } */ + } + closedir( dir ); + + return ret; +} + +static void __remove_dir( void *data, void *user_data ) +{ + const char *dname = (const char *)data; + + if( dname && is_dir_empty( (const char *)dname ) ) + { + (void)rmdir( dname ); + } +} + +static void remove_package( void ) +{ + /* Try to change CWD to the ROOT directory: */ + (void)chdir( (const char *)root ); + + if( links ) { dlist_foreach( links, __remove_link, NULL ); } + + if( files ) { dlist_foreach( files, __remove_file, NULL ); } + + if( dirs ) + { + dirs = dlist_sort( dirs, __cmp_list_items ); + dirs = dlist_reverse( dirs ); + dlist_foreach( dirs, __remove_dir, NULL ); + } + + /* Try to change CWD to the CURRENT directory: */ + (void)chdir( (const char *)curdir ); +} +/* + End of removal functions. + ********************************************************/ + +static void post_remove_routine( void ) +{ + pid_t p = (pid_t) -1; + int rc; + + int len = 0; + char *cmd = NULL; + + 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, + "cd %s && %s/.INSTALL post_remove %s > /dev/null 2>&1", + root, rtmpdir, pkgver ); + if( len == 0 || len == PATH_MAX - 1 ) + { + FATAL_ERROR( "Cannot run post-remove script for '%s-%s' package", pkgname, pkgver ); + } + p = sys_exec_command( cmd ); + rc = sys_wait_command( p, (char *)NULL, PATH_MAX ); + + free( cmd ); + + if( rc != 0 ) + { + exit_status = 46; + + if( remove_mode != CONSOLE ) + { +#if defined( HAVE_DIALOG ) + info_pkg_box( "Remove:", pkgname, pkgver, NULL, + "\n\\Z1Post-remove script returned error status.\\Zn\n", 5, 0, 0 ); +#else + fprintf( stdout, "\nPost-remove script of '%s-%s' returned error status.\n\n", pkgname, pkgver ); +#endif + } + else + { + fprintf( stdout, "\nPost-remove script of '%s-%s' returned error status.\n\n", pkgname, pkgver ); + } + + if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); } + free_resources(); + exit( exit_status ); + } +} + +static void finalize_removal( void ) +{ + pid_t p = (pid_t) -1; + int rc; + + int len = 0; + char *cmd = NULL, *tmp = NULL; + + cmd = (char *)malloc( (size_t)PATH_MAX ); + if( !cmd ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)cmd, PATH_MAX ); + + tmp = (char *)malloc( (size_t)PATH_MAX ); + if( !tmp ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)tmp, PATH_MAX ); + + /********************************************* + Decrement references in the Setup Database: + */ + if( group ) + len = snprintf( &cmd[0], PATH_MAX, + "%s/chrefs --operation=dec --destination=%s %s/%s > /dev/null 2>&1", + selfdir, pkgs_path, group, basename( (char *)pkglog_fname ) ); + else + len = snprintf( &cmd[0], PATH_MAX, + "%s/chrefs --operation=dec --destination=%s %s > /dev/null 2>&1", + selfdir, pkgs_path, basename( (char *)pkglog_fname ) ); + if( len == 0 || len == PATH_MAX - 1 ) + { + FATAL_ERROR( "Cannot decrement '%s-%s' package references", pkgname, pkgver ); + } + p = sys_exec_command( cmd ); + rc = sys_wait_command( p, (char *)NULL, PATH_MAX ); + if( (rc != 0) && !ignore_chrefs_errors ) + { + free( cmd ); + free( tmp ); + + exit_status = 48; + + if( remove_mode != CONSOLE ) + { +#if defined( HAVE_DIALOG ) + info_pkg_box( "Remove:", pkgname, pkgver, NULL, + "\n\\Z1Cannot decrement package references in Setup Database.\\Zn\n", 5, 0, 0 ); +#else + fprintf( stdout, "\nCannot decrement '%s-%s' package references in Setup Database.\n\n", pkgname, pkgver ); +#endif + } + else + { + if( !quiet ) + { + fprintf( stdout, "\nCannot decrement '%s-%s' package references in Setup Database.\n\n", pkgname, pkgver ); + } + } + + if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); } + free_resources(); + exit( exit_status ); + } + + /***************************************************** + Backup PKGLOG file into removed-packages directory: + */ + bzero( (void *)tmp, PATH_MAX ); + + if( group ) + (void)sprintf( &tmp[0], "%s/%s/", rempkgs_path, group ); + else + (void)sprintf( &tmp[0], "%s/", rempkgs_path ); + + if( _mkdir_p( tmp, S_IRWXU | S_IRWXG | S_IRWXO ) != 0 ) + { + FATAL_ERROR( "Cannot access '/%s' directory", REMOVED_PKGS_PATH ); + } + + bzero( (void *)cmd, PATH_MAX ); + + len = snprintf( &cmd[0], PATH_MAX, + "mv %s %s > /dev/null 2>&1", + pkglog_fname, (const char *)&tmp[0] ); + if( len == 0 || len == PATH_MAX - 1 ) + { + FATAL_ERROR( "Cannot backup '%s' pkglog file", basename( (char *)pkglog_fname ) ); + } + p = sys_exec_command( cmd ); + rc = sys_wait_command( p, (char *)NULL, PATH_MAX ); + + free( cmd ); + + if( rc != 0 ) + { + free( tmp ); + + exit_status = 47; + + if( remove_mode != CONSOLE ) + { +#if defined( HAVE_DIALOG ) + info_pkg_box( "Remove:", pkgname, pkgver, NULL, + "\n\\Z1Cannot backup PKGLOG file.\\Zn\n", 5, 0, 0 ); +#else + fprintf( stdout, "\nCannot backup '%s' pkglog file.\n\n", basename( (char *)pkglog_fname ) ); +#endif + } + else + { + if( !quiet ) + { + fprintf( stdout, "\nCannot backup '%s' pkglog file.\n\n", basename( (char *)pkglog_fname ) ); + } + } + + if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); } + free_resources(); + exit( exit_status ); + } + + /**************************************** + Remove group directory if it is empty: + */ + bzero( (void *)tmp, PATH_MAX ); + + if( group ) + { + (void)sprintf( &tmp[0], "%s/%s/", pkgs_path, group ); + + const char *dir = (const char *)&tmp[0]; + if( is_dir_empty( dir ) ) + { + (void)rmdir( dir ); + } + } + + free( tmp ); +} + + +static int ask_for_remove( int prev ) +{ + int ret = 0; /* continue removal */ +#if defined( HAVE_DIALOG ) + /****************************************************** + Ask for remove dialog shown only in MENUDIALOG mode: + */ + if( (remove_mode == MENUDIALOG) ) + { + if( prev < 0 ) + { + char *msg = NULL; + + msg = (char *)malloc( (size_t)PATH_MAX ); + if( !msg ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)msg, PATH_MAX ); + + (void)sprintf( &msg[0], "\nPrevious version '\\Z4%s\\Zn' of requested package installed.\n" + "\n\\Z1Remove a previous vesion?\\Zn\n", pkgver ); + + ret = ask_remove_box( "Remove:", pkgname, requested_version, (const char *)&msg[0], 9, 0, 0 ); + + free( msg ); + } + else if( prev > 0 ) + { + char *msg = NULL; + + msg = (char *)malloc( (size_t)PATH_MAX ); + if( !msg ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)msg, PATH_MAX ); + + (void)sprintf( &msg[0], "\nA newer version '\\Z4%s\\Zn' of requested package installed.\n" + "\n\\Z1Remove a newer vesion?\\Zn\n", pkgver ); + + ret = ask_remove_box( "Remove:", pkgname, requested_version, (const char *)&msg[0], 9, 0, 0 ); + + free( msg ); + } + else + { + ret = ask_remove_box( "Remove:", pkgname, pkgver, description, 18, 0, 0 ); + } + } + + if( ret ) + { + info_pkg_box( "Remove:", pkgname, pkgver, NULL, + "\nPackage removal terminated by user.\n", 5, 0, 0 ); + } +#endif + return ret; +} + + +static void show_removal_progress( void ) +{ + if( remove_mode != CONSOLE ) + { +#if defined( HAVE_DIALOG ) + info_pkg_box( "Remove:", pkgname, pkgver, NULL, + description, 16, 1, 0 ); +#else + fprintf( stdout, "\n Remobe: %s-%s ...\n", pkgname, pkgver ); + /************************************************* + Ruler: 68 characters + 2 spaces left and right: + + | ----handy-ruler----------------------------------------------------- | */ + fprintf( stdout, "|======================================================================|\n" ); + fprintf( stdout, "%s\n", description ); + fprintf( stdout, "|======================================================================|\n\n" ); +#endif + } + else + { + if( !quiet ) + { + fprintf( stdout, "\n Remove: %s-%s ...\n", pkgname, pkgver ); + /************************************************* + Ruler: 68 characters + 2 spaces left and right: + + | ----handy-ruler----------------------------------------------------- | */ + fprintf( stdout, "|======================================================================|\n" ); + fprintf( stdout, "%s\n", description ); + fprintf( stdout, "|======================================================================|\n\n" ); + } + } +} + + +static void read_pkginfo( const char *pkginfo_fname ) +{ + char *ln = NULL; + char *line = NULL; + + FILE *pkginfo = NULL; + + if( pkginfo_fname != NULL ) + { + pkginfo = fopen( (const char *)pkginfo_fname, "r" ); + if( !pkginfo ) + { + FATAL_ERROR( "Cannot open %s file", pkginfo_fname ); + } + } + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + while( (ln = fgets( line, PATH_MAX, pkginfo )) ) + { + char *match = NULL; + + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + skip_eol_spaces( ln ); /* remove spaces at end-of-line */ + + if( (match = strstr( ln, "pkgname" )) && match == ln ) { + char *p = index( match, '=' ) + 1; + if( p != NULL ) + { + if( pkgname ) { free( pkgname ); } + pkgname = skip_spaces( p ); + } + } + if( (match = strstr( ln, "pkgver" )) && match == ln ) { + char *p = index( match, '=' ) + 1; + if( p != NULL ) + { + if( pkgver ) { free( pkgver ); } + pkgver = skip_spaces( p ); + } + } + if( (match = strstr( ln, "arch" )) && match == ln ) { + char *p = index( match, '=' ) + 1; + if( p != NULL ) + { + if( arch ) { free( arch ); } + arch = skip_spaces( p ); + } + } + if( (match = strstr( ln, "distroname" )) && match == ln ) { + char *p = index( match, '=' ) + 1; + if( p != NULL ) + { + if( distroname ) { free( distroname ); } + distroname = skip_spaces( p ); + } + } + if( (match = strstr( ln, "distrover" )) && match == ln ) { + char *p = index( match, '=' ) + 1; + if( p != NULL ) + { + if( distrover ) { free( distrover ); } + distrover = skip_spaces( p ); + } + } + + if( (match = strstr( ln, "group" )) && match == ln ) { + char *p = index( match, '=' ) + 1; + if( p != NULL ) + { + if( group ) { free( group ); } + group = skip_spaces( p ); + } + } + + if( (match = strstr( ln, "short_description" )) && match == ln ) { + char *p = index( match, '=' ) + 1; + if( p != NULL ) + { + char *b = index( p, '"'), + *e = rindex( p, '"'); + if( b && e && ( b != e ) ) + { + p = ++b; *e = '\0'; + if( short_description ) { free( short_description ); } + short_description = strdup( (const char *)p ); + } + } + } + if( (match = strstr( ln, "url" )) && match == ln ) { + char *p = index( match, '=' ) + 1; + if( p != NULL ) + { + if( url ) { free( url ); } + url = skip_spaces( p ); + } + } + if( (match = strstr( ln, "license" )) && match == ln ) { + char *p = index( match, '=' ) + 1; + if( p != NULL ) + { + if( license ) { free( license ); } + license = skip_spaces( p ); + } + } + + if( (match = strstr( ln, "uncompressed_size" )) && match == ln ) { + char *p = index( match, '=' ) + 1; + if( p != NULL ) + { + if( uncompressed_size ) { free( uncompressed_size ); } + uncompressed_size = skip_spaces( p ); + } + } + if( (match = strstr( ln, "total_files" )) && match == ln ) { + char *p = index( match, '=' ) + 1; + if( p != NULL ) + { + if( total_files ) { free( total_files ); } + total_files = skip_spaces( p ); + } + } + } + + free( line ); + + if( !pkgname || !pkgver || !arch || !distroname || !distrover ) + { + FATAL_ERROR( "Invalid input .PKGINFO file" ); + } + + fclose( pkginfo ); +} + + +/*************************************************************** + Probe functions: + */ +static void _probe_pkglog( const char *dirpath, const char *grp ) +{ + DIR *dir; + char *path; + size_t len; + + struct stat path_sb, entry_sb; + struct dirent *entry; + + if( pkglog_fname ) return; + + if( stat( dirpath, &path_sb ) == -1 ) + { + FATAL_ERROR( "%s: Cannot stat Setup Database or destination directory", dirpath ); + } + + if( S_ISDIR(path_sb.st_mode) == 0 ) + { + FATAL_ERROR( "%s: Setup Database or destination is not a directory", dirpath ); + } + + if( (dir = opendir(dirpath) ) == NULL ) + { + FATAL_ERROR( "Canot access %s directory: %s", dirpath, strerror( errno ) ); + } + + 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_ISREG(entry_sb.st_mode) ) + { + char *match = NULL; + char *pkglog = basename( path ); + + if( (match = strstr( pkglog, (const char *)basename( pkg_fname ) )) && match == pkglog ) + { + char *buf = NULL, *p = NULL, *q = NULL; + + p = q = buf = strdup( (const char *)pkglog ); + ++p; + while( *p != '\0' && !isblank(*p) && !(*q == '-' && isdigit(*p)) ) + { + /* package version starts with a number and separated by '-' */ + ++p; ++q; + } + *(--p) = '\0'; + + /******************************************************* + We have to make sure that the name we are looking for + is not shorter than the name of the found package. + */ + if( strlen(pkg_fname) >= strlen(buf) ) + { + + pkglog_fname = strdup( (const char *)path ); + free( buf ); + closedir( dir ); + return; + } + free( buf ); + } + } + if( S_ISDIR(entry_sb.st_mode) && grp == NULL ) + { + _probe_pkglog( (const char *)path, (const char *)entry->d_name ); + } + } + /* else { stat() returns error code; errno is set; and we have to continue the loop } */ + } + + closedir( dir ); +} + +/*********************************************************** + probe_package(): + --------------- + */ +static char *probe_package( void ) +{ + char *ret = NULL; + + _probe_pkglog( (const char *)pkgs_path, NULL ); + if( pkglog_fname ) + { + free( pkg_fname ); + ret = pkg_fname = pkglog_fname; + } + + return ret; +} +/* + Enf of Probe functions. + ***********************************************************/ + +/*********************************************************** + Find functions: + */ +static void _search_pkglog( const char *dirpath, const char *grp ) +{ + DIR *dir; + char *path; + size_t len; + + struct stat path_sb, entry_sb; + struct dirent *entry; + + char *pname = (char *)dirpath + strlen( root ); /* do not remove leading '/' */ + + if( stat( dirpath, &path_sb ) == -1 ) + { + FATAL_ERROR( "%s: Cannot stat Setup Database or group directory", pname ); + } + + if( S_ISDIR(path_sb.st_mode) == 0 ) + { + FATAL_ERROR( "%s: Setup Database or group is not a directory", pname ); + } + + if( (dir = opendir(dirpath) ) == NULL ) + { + FATAL_ERROR( "Canot access %s directory: %s", pname, strerror( errno ) ); + } + + 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_ISREG(entry_sb.st_mode) ) + { + char *match = NULL, *name = basename( path ); + + if( (match = strstr( name, pkgname )) && match == name ) + { + /**************************************************************** + Здесь мы еще должны проверить, что найденный пакет не имеет + более длинное имя, которое начинается с имени искомого пакета. + Полагаясь на факт, что версия может начинаться только с цифры, + мы пропускаем символ '-', разделяющий имя и версию пакета, + а затем проверяем начальный символ версии: + */ + if( *(name + strlen( pkgname )) == '-' && isdigit( *(name + strlen( pkgname ) + 1) ) ) + { + pkglog_fname = strdup( (const char *)path ); + closedir( dir ); + return; + } + } + } + if( S_ISDIR(entry_sb.st_mode) && grp == NULL ) + { + /************************************************************************** + NOTE: + In the Setup Database can be only one package with the same pkgname + but in different groups. For example, the package named 'cairo' + has two instance: libs/cairo-1.14.6 and xlibs/cairo-1.14.6. During + system installation the package libs/cairo-1.14.6 installed first + and then updated by xlibs/cairo-1.14.6 and PKGLOG of libs/cairo-1.14.6 + moved from /var/log/radix/packages to /var/log/radix/removed-packages. + + So here we have to look for the PKGLOG in all group directories: + */ + _search_pkglog( (const char *)path, (const char *)entry->d_name ); + } + } + /* else { stat() returns error code; errno is set; and we have to continue the loop } */ + } + + closedir( dir ); +} + +static char *find_package( void ) +{ + char *ret = NULL; + + _search_pkglog( (const char *)pkgs_path, NULL ); + if( pkglog_fname ) + { + free( pkg_fname ); + ret = pkg_fname = pkglog_fname; + } + + return ret; +} +/* + Enf of Find functions. + ***********************************************************/ + + +/*********************************************************** + check_input_package(): + --------------------- + + Возвращает: + -1 если пакет установлен, но его версия меньше + запрашиваемого, + 0 если версия установленного и запрашиваемого равны, + 1 если пакет установлен, но его версия больше + запрашиваемого. + + В случае возврата -1 или 1, устанавливается переменная + requested_version, равная версии пакета который запросили + на удаление. + + Если пакет не установлен, осуществляется выход со статусом 30. + */ +static int check_input_package( void ) +{ + struct stat st; + char *fname = pkg_fname; + + enum _input_type type = IFMT_UNKNOWN; + char uncompress = '\0'; + + int ret = 0; + + bzero( (void *)&st, sizeof( struct stat ) ); + + if( stat( (const char *)fname, &st ) == -1 ) + { + /************************************************* + Specified pkg_fname is not a file or directory. + Try to find installed package with name equal + to pkg_fname: + */ + fname = NULL; + fname = probe_package(); + if( !fname ) + { + if( remove_mode != CONSOLE ) + { +#if defined( HAVE_DIALOG ) + info_pkg_box( "Remove:", basename( pkg_fname ), NULL, NULL, + "\nPackage is not installed.\n", 5, 0, 0 ); +#else + fprintf( stdout, "\nPackage '%s' is not installed.\n\n", basename( pkg_fname ) ); +#endif + } + else + { + if( !quiet ) fprintf( stdout, "Specified package '%s' is not installed.\n\n", basename( pkg_fname ) ); + } + + exit_status = 30; /* Package is not installed: install */ + + if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); } + free_resources(); + + exit( exit_status ); + } + } + else + { + if( S_ISREG(st.st_mode) ) + { + pid_t p = (pid_t) -1; + int rc; + + int len = 0; + char *tmp= NULL, *cmd = NULL; + + tmp = (char *)malloc( (size_t)PATH_MAX ); + if( !tmp ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)tmp, PATH_MAX ); + + (void)sprintf( &tmp[0], "%s", tmpdir ); + if( _mkdir_p( tmp, S_IRWXU | S_IRWXG | S_IRWXO ) != 0 ) + { + FATAL_ERROR( "Cannot get PKGINFO from '%s' file", basename( (char *)fname ) ); + } + + 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/pkginfo -d %s -o pkginfo %s > /dev/null 2>&1", + selfdir, tmp, fname ); + if( len == 0 || len == PATH_MAX - 1 ) + { + FATAL_ERROR( "Cannot get PKGINFO from %s file", basename( (char *)fname ) ); + } + p = sys_exec_command( cmd ); + rc = sys_wait_command( p, (char *)NULL, PATH_MAX ); + if( rc != 0 ) + { + FATAL_ERROR( "Cannot get PKGINFO from '%s' file", basename( (char *)fname ) ); + } + + (void)strcat( tmp, "/.PKGINFO" ); + read_pkginfo( (const char *)&tmp[0] ); + (void)unlink( (const char *)&tmp[0] ); /* :remove unnecessary .PKGINFO file */ + *(strstr( tmp, "/.PKGINFO" )) = '\0'; /* :restore 'tmpdir' in tmp[] buffer */ + + requested_version = strdup( (const char *)pkgver ); + + fname = NULL; + fname = find_package(); + if( !fname ) + { + if( remove_mode != CONSOLE ) + { +#if defined( HAVE_DIALOG ) + info_pkg_box( "Remove:", basename( pkg_fname ), NULL, NULL, + "\nPackage is not installed.\n", 5, 0, 0 ); +#else + fprintf( stdout, "\nPackage '%s' is not installed.\n\n", basename( pkg_fname ) ); +#endif + } + else + { + if( !quiet ) fprintf( stdout, "Specified package '%s' is not installed.\n\n", basename( pkg_fname ) ); + } + + exit_status = 30; /* Package is not installed: install */ + + if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); } + free_resources(); + + exit( exit_status ); + } + + free( cmd ); + free( tmp ); + } + else + { + FATAL_ERROR( "Input %s file is not a regular file", basename( (char *)fname ) ); + } + } + + /* check pkg_fname again: */ + if( stat( (const char *)fname, &st ) == -1 ) + { + FATAL_ERROR( "Cannot access input '%s' file: %s", fname, strerror( errno ) ); + } + + type = check_input_file( &uncompress, fname ); + if( type == IFMT_UNKNOWN ) + { + FATAL_ERROR( "Unknown format of input '%s' file", fname ); + } + + if( S_ISREG(st.st_mode) ) + { + pid_t p = (pid_t) -1; + int rc; + + int len = 0; + char *tmp= NULL, *cmd = NULL; + + tmp = (char *)malloc( (size_t)PATH_MAX ); + if( !tmp ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)tmp, PATH_MAX ); + + (void)sprintf( &tmp[0], "%s/to-remove", tmpdir ); + if( _mkdir_p( tmp, S_IRWXU | S_IRWXG | S_IRWXO ) != 0 ) + { + FATAL_ERROR( "Cannot get PKGINFO from '%s' file", basename( (char *)fname ) ); + } + rtmpdir = strdup( (const char *)&tmp[0] ); + + 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/pkginfo -d %s -o pkginfo,description,install-script,restore-links,filelist %s > /dev/null 2>&1", + selfdir, tmp, fname ); + if( len == 0 || len == PATH_MAX - 1 ) + { + FATAL_ERROR( "Cannot get PKGINFO from %s file", basename( (char *)fname ) ); + } + p = sys_exec_command( cmd ); + rc = sys_wait_command( p, (char *)NULL, PATH_MAX ); + if( rc != 0 ) + { + FATAL_ERROR( "Cannot get PKGINFO from '%s' file", basename( (char *)fname ) ); + } + + (void)strcat( tmp, "/.PKGINFO" ); + read_pkginfo( (const char *)&tmp[0] ); + *(strstr( tmp, "/.PKGINFO" )) = '\0'; /* :restore tmpdir in tmp[] buffer */ + + free( cmd ); + free( tmp ); + + if( requested_version ) + { + ret = cmp_version( (const char *)pkgver, (const char *)requested_version ); + } + } + else + { + FATAL_ERROR( "Input %s file is not a regular file", basename( (char *)fname ) ); + } + + return ret; +} + + + +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 = strdup( 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 = strdup( 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 ); + + 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" ); + } + + { + int status = 0; + + /********************************************************** + Fill pkginfo data and put or replace pkglog into tmpdir: + */ + status = check_input_package(); + + read_filelist(); + read_restorelinks(); + read_description(); + + if( ask_for_remove( status ) ) + { + /* Terminate removal: */ + if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); } + free_resources(); + exit( exit_status ); + } + } + + show_removal_progress(); + + /************ + DO REMOVE: + */ + pre_remove_routine(); + remove_package(); + post_remove_routine(); + finalize_removal(); + + if( remove_mode != CONSOLE ) + { +#if defined( HAVE_DIALOG ) + info_pkg_box( "Remove:", pkgname, pkgver, NULL, + "\nPackage has been removed.\n", 5, 0, 0 ); +#else + fprintf( stdout, "\nPackage '%s-%s' has been removed.\n\n", pkgname, pkgver ); +#endif + } + else + { + if( !quiet ) + { + fprintf( stdout, "\nPackage '%s-%s' has been removed.\n\n", pkgname, pkgver ); + } + } + + setup_log( "Package '%s-%s' has been removed", pkgname, pkgver ); + + if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); } + free_resources(); + + exit( exit_status ); +} |