diff options
Diffstat (limited to 'src/check-db-integrity.c')
-rw-r--r-- | src/check-db-integrity.c | 3241 |
1 files changed, 3241 insertions, 0 deletions
diff --git a/src/check-db-integrity.c b/src/check-db-integrity.c new file mode 100644 index 0000000..87120b8 --- /dev/null +++ b/src/check-db-integrity.c @@ -0,0 +1,3241 @@ + +/********************************************************************** + + 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 <msglog.h> +#include <wrapper.h> +#include <system.h> +#include <dlist.h> +#include <pkglist.h> + +#define PROGRAM_NAME "check-db-integrity" + +#include <defs.h> + + +char *program = PROGRAM_NAME; +char *root = NULL, *pkgs_path = NULL, *errlog_fname = NULL, + *tmpdir = NULL; + +int close_log_file = 0; + +int exit_status = EXIT_SUCCESS; /* errors counter */ +char *selfdir = NULL; + +int __done = 0, __child = 0; + +enum _input_type { + IFMT_PKG = 0, + IFMT_LOG, + + IFMT_UNKNOWN +} input_format = IFMT_PKG; + +enum _priority priority = REQUIRED; + + +void free_resources() +{ + if( root ) { free( root ); root = NULL; } + if( pkgs_path ) { free( pkgs_path ); pkgs_path = NULL; } + if( errlog_fname ) { free( errlog_fname ); errlog_fname = NULL; } + + if( selfdir ) { free( selfdir ); selfdir = NULL; } + + if( close_log_file ) + { + (void)fflush( errlog ); + fclose( errlog ); + } + + free_tarballs(); + free_packages(); +} + +void usage() +{ + free_resources(); + + fprintf( stdout, "\n" ); + fprintf( stdout, "Usage: %s [options] [pkglogs path]\n", program ); + fprintf( stdout, "\n" ); + fprintf( stdout, "Check Setup Database integrity - is a procedure for checking data\n" ); + fprintf( stdout, "integrity and correcting errors. This procedure removes invalid\n" ); + fprintf( stdout, "inter-package links, and also outputs the lists of packages that\n" ); + fprintf( stdout, "need to be installed to restore system health.\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, " -r,--root=<DIR> Target rootfs path.\n" ); + fprintf( stdout, " -l,--log=<LOGFILE> Log file name.\n" ); + + fprintf( stdout, "\n" ); + fprintf( stdout, "Optional parameter:\n" ); + fprintf( stdout, " [pkglogs path] The PKGLOGs path in the Setup Database.\n" ); + fprintf( stdout, "\n" ); + fprintf( stdout, "If the [pkglogs path] is defined, then LOG information outputs to\n" ); + fprintf( stdout, "stderr and options --root, and --log are ignored.\n" ); + fprintf( stdout, "\n" ); + fprintf( stdout, "By default, the Setup Database is located in the\n" ); + fprintf( stdout, " '/%s/'\n", SETUP_DB_PATH ); + fprintf( stdout, "directory, the PKGLOGs files of installed packages are located in the\n" ); + fprintf( stdout, " '/%s/'\n", PACKAGES_PATH ); + fprintf( stdout, "directory; the log of this procedure is written to the\n" ); + fprintf( stdout, " '/%s/%s.log'\n", LOG_PATH, program ); + fprintf( stdout, "file.\n" ); + fprintf( stdout, "\n" ); +/* + |==================================================================| + Check Setup Database integrity - это процедура проверки целостности + данных и исправления ошибок. Данная процедура удаляет невалидные + межпакетные ссылки, а также выдает список пакетов, которые необходимо + инсталлировать для восстановления работоспособности системы. + + По умолчанию инсталляционная база находится в каталоге + + '/var/log/radix/' , + + описания инсталлированных пакетов находятся в каталоге + + '/var/log/radix/packages/' ; + + лог записывается в файл + + '/var/log/radix/check-db-integrity.log' . + |==================================================================| + */ + + 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; +} + + +/******************************************** + LOCK FILE functions: + */ +static int __lock_file( FILE *fp ) +{ + int fd = fileno( fp ); + + if( flock( fd, LOCK_EX ) == -1 ) + { + return -1; + /* + Мы не проверяем errno == EWOULDBLOCK, так какданная ошибка + говорит о том что файл заблокирован другим процессом с флагом + LOCK_NB, а мы не собираемся циклически проверять блокировку. + У нас все просто: процесс просто ждет освобождения дескриптора + и не пытается во время ожидания выполнять другие задачи. + */ + } + return fd; +} + +static void __unlock_file( int fd ) +{ + if( fd != -1 ) flock( fd, LOCK_UN ); + /* + Здесь, в случае ошибки, мы не будем выводить + никаких сообщений. Наш процесс выполняет простую + атомарную задачу и, наверное, завершится в скором + времени, освободив все дескрипторы. + */ +} +/* + End of LOCK FILE functions. + ********************************************/ + + +void fatal_error_actions( void ) +{ + logmsg( errlog, MSG_NOTICE, "Free resources on FATAL error..." ); + if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); } + free_resources(); +} + +void sigint( int signum ) +{ + (void)signum; + + if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); } + free_resources(); +} + +void sigchld( int signum ) +{ + pid_t pid = 0; + int status; + + (void)signum; + + while( (pid = waitpid( -1, &status, WNOHANG )) > 0 ) + { + ; /* One of children with 'pid' is terminated */ + + if( WIFEXITED( status ) ) + { + if( (int) WEXITSTATUS (status) > 0 ) + { + ++exit_status; /* printf( "Child %d returned non zero status: %d\n", pid, (int)WEXITSTATUS (status) ); */ + } + else + { + ; /* printf( "Child %d terminated with status: %d\n", pid, (int)WEXITSTATUS (status) ); */ + } + } + else if( WIFSIGNALED( status ) ) + { + ++exit_status; /* printf( "Child %d terminated on signal: %d\n", pid, WTERMSIG( status ) ); */ + } + else + { + ++exit_status; /* printf( "Child %d terminated on unknown reason\n", pid ); */ + } + + } + + if( pid == -1 && errno == ECHILD ) + { + /* No child processes: */ + __done = 1; + } + return; +} + + +static void set_signal_handlers() +{ + struct sigaction sa; + sigset_t set; + + memset( &sa, 0, sizeof( sa ) ); + sa.sa_handler = sigint; /* TERM, INT */ + sa.sa_flags = SA_RESTART; + sigemptyset( &set ); + sigaddset( &set, SIGTERM ); + sigaddset( &set, SIGINT ); + sa.sa_mask = set; + sigaction( SIGTERM, &sa, NULL ); + sigaction( SIGINT, &sa, NULL ); + + /* System V fork+wait does not work if SIGCHLD is ignored */ + memset( &sa, 0, sizeof( sa ) ); + sa.sa_handler = sigchld; /* CHLD */ + sa.sa_flags = SA_RESTART; + sigemptyset( &set ); + sigaddset( &set, SIGCHLD ); + sa.sa_mask = set; + sigaction( SIGCHLD, &sa, NULL ); + + memset( &sa, 0, sizeof( sa ) ); /* ignore SIGPIPE */ + sa.sa_handler = SIG_IGN; + sa.sa_flags = 0; + sigaction( SIGPIPE, &sa, NULL ); +} + + +static enum _input_type check_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[] ) +{ + const char* short_options = "hvr:l:"; + + const struct option long_options[] = + { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'v' }, + { "root", required_argument, NULL, 'r' }, + { "log", required_argument, NULL, 'l' }, + { NULL, 0, NULL, 0 } + }; + + int ret; + int option_index = 0; + + while( (ret = getopt_long( argc, argv, short_options, long_options, &option_index )) != -1 ) + { + switch( ret ) + { + case 'h': + { + usage(); + break; + } + case 'v': + { + version(); + break; + } + + case 'r': + { + if( optarg != NULL ) + { + root = xstrdup( (const char *)optarg ); + remove_trailing_slash( root ); + } + else + /* option is present but without value */ + usage(); + break; + } + case 'l': + { + if( optarg != NULL ) + { + errlog_fname = xstrdup( (const char *)optarg ); + } + else + /* option is present but without value */ + usage(); + break; + } + + case '?': default: + { + usage(); + break; + } + } + } + + + if( optind < argc ) + { + /* + last command line argument assumes as the packages directory + in the SETUP_DB_PATH. If this argument is defined then we + ignore --root, --log options. + */ + pkgs_path = xstrdup( (const char *)argv[optind] ); + remove_trailing_slash( pkgs_path ); + + /* output LOG into stderr*/ + if( root ) { free( root ); root = NULL; } + if( errlog_fname ) { free( errlog_fname ); errlog_fname = NULL; } + errlog_fname = xstrdup( "-" ); + } + + + if( !pkgs_path ) + { + 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 ); + + if( !root ) + { + buf[0] = '/'; buf[1] = '\0'; + root = xstrdup( (const char *)&buf[0] ); + } + else + { + int len = strlen( root ); + + (void)strcpy( buf, (const char *)root ); + if( buf[ len - 1 ] != '/' ) + { + buf[len] = '/'; buf[len+1] = '\0'; + } + } + + (void)strcat( buf, PACKAGES_PATH ); + 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) ) + { + pkgs_path = xstrdup( (const char *)&buf[0] ); + free( buf ); + } + else + { + FATAL_ERROR( "Defined --root '%s' is not a directory", buf ); + } + + } /* End if( !pkgs_path ) */ + + if( !errlog_fname ) + { + 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 ); + + if( !root ) + { + buf[0] = '/'; buf[1] = '\0'; + } + else + { + int len = strlen( root ); + + (void)strcpy( buf, (const char *)root ); + if( buf[ len - 1 ] != '/' ) + { + buf[len] = '/'; buf[len+1] = '\0'; + } + } + + (void)strcat( buf, LOG_PATH ); + 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) ) + { + (void)strcat( buf, LOG_FILE ); + errlog_fname = xstrdup( (const char *)&buf[0] ); + free( buf ); + } + else + { + FATAL_ERROR( "The path '%s' is not a directory", buf ); + } + } + else /* errlog_fname is defined */ + { + struct stat st; + char *buf = NULL, *dir = 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 ); + + strncpy( buf, (const char *)errlog_fname, (size_t)PATH_MAX ); + buf[ PATH_MAX - 1] = '\0'; + + dir = dirname( buf ); + + if( _mkdir_p( (const char *)dir, S_IRWXU | S_IRWXG | S_IRWXO ) != 0 ) + { + FATAL_ERROR( "Cannot access '%s' directory", buf ); + } + + free( buf ); + + } /* End if( !errlog_fname ) */ + +} + + +/*************************************************************** + Copy functions: + */ +static void _copy_pkglog( const char *group, const char *fname ) +{ + enum _input_type type = IFMT_UNKNOWN; + char uncompress = '\0'; + + type = check_input_file( &uncompress, fname ); + + if( type == IFMT_LOG ) + { + 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 ); + + if( group ) { (void)sprintf( &tmp[0], "%s/%s", tmpdir, group ); } + else { (void)sprintf( &tmp[0], "%s", tmpdir ); } + + if( _mkdir_p( tmp, S_IRWXU | S_IRWXG | S_IRWXO ) != 0 ) + { + LOG( "ERROR: Cannot copy '%s' PKGLOG file", basename( (char *)fname ) ); + exit_status += 1; + free( tmp ); + return; + } + + 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, "cp %s %s/ > /dev/null 2>&1", fname, tmp ); + if( len == 0 || len == PATH_MAX - 1 ) + { + FATAL_ERROR( "Cannot copy %s PKGLOG file", basename( (char *)fname ) ); + } + (void)sys_exec_command( cmd ); + ++__child; + + free( tmp ); + free( cmd ); + } +} + +static void _search_pkglogs( const char *dirpath, const char *grp ) +{ + DIR *dir; + char *path; + size_t len; + + struct stat path_sb, entry_sb; + struct dirent *entry; + + 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) ) + { + _copy_pkglog( grp, (const char *)path ); + } + if( S_ISDIR(entry_sb.st_mode) && grp == NULL ) + { + _search_pkglogs( (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 ); +} + +/*********************************************************** + copy_pkglogs() - returns number of copied PKGLOGS or 0 if + no PKGLOGS found in the destination + directory (SETUP_DB_PATH). + The exit_status has been set. + */ +int copy_pkglogs( void ) +{ + int ret = 0; + + __done = 0; __child = 0; + + _search_pkglogs( (const char *)pkgs_path, NULL ); + + if( __child > 0 ) + { + while( !__done ) usleep( 1 ); + ret = __child; + } + + __done = 0; __child = 0; + + return ret; +} +/* + Enf of Copy functions. + ***************************************************************/ + + +/*********************************************************** + 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( xstrdup( (const char *)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'; } +} + +static size_t read_usize( char *s ) +{ + size_t size = 0; + size_t mult = 1; + double sz = 0.0; + + char suffix; + char *q, *p = (char *)0; + + if( !s || *s == '\0' ) return size; + + p = s; + + while( (*p == ' ' || *p == '\t') && *p != '\0' ) { ++p; } q = p; + while( *q != ' ' && *q != '\t' && *q != '\0' ) { ++q; } *q = '\0'; + + if( *p == '\0' ) return size; + + --q; + suffix = *q; + switch( suffix ) + { + /* by default size calculates in KiB - 1024 Bytes (du -s -h .) */ + case 'G': + case 'g': + mult = 1024 * 1024; + *q = '\0'; + break; + case 'M': + case 'm': + mult = 1024; + *q = '\0'; + break; + case 'K': + case 'k': + *q = '\0'; + break; + default: + break; + } + + if( sscanf( p, "%lg", &sz ) != 1 ) return size; + + return (size_t)round( sz * (double)mult ); +} + +static int read_total_files( char *s ) +{ + int n = 0; + char *q, *p = (char *)0; + + if( !s || *s == '\0' ) return n; + + p = s; + + while( (*p == ' ' || *p == '\t') && *p != '\0' ) { ++p; } q = p; + while( *q != ' ' && *q != '\t' && *q != '\0' ) { ++q; } *q = '\0'; + + if( *p == '\0' ) return n; + + if( sscanf( p, "%u", &n ) != 1 ) return 0; + + return n; +} + +static void get_short_description( char *buf, const char *line ) +{ + char *s, *p, *q; + + if( buf ) { buf[0] = '\0'; s = buf; } + if( !line || line[0] == '\0' ) return; + + p = index( line, '(' ); + q = index( line, ')' ); + if( p && q && q > p ) + { + ++p; + while( *p && p < q ) + { + *s = *p; + ++p; ++s; + } + *s = '\0'; + } + else + { + /* + If short description declaration is incorrect at first line + of description; then we take whole first line of description: + */ + p = index( line, ':' ); ++p; + while( (*p == ' ' || *p == '\t') && *p != '\0' ) { ++p; } + strcpy( buf, p ); + } +} + + +static int get_references_section( int *start, int *stop, unsigned int *cnt, FILE *log ) +{ + int ret = -1, found = 0; + + if( !start || !stop || !cnt ) return ret; + + if( log != NULL ) + { + char *ln = NULL; + char *line = NULL; + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + ++ret; + *start = 0; *stop = 0; + + while( (ln = fgets( line, PATH_MAX, log )) ) + { + char *match = NULL; + + if( (match = strstr( ln, "REFERENCE COUNTER:" )) && match == ln ) /* at start of line only */ + { + *start = ret + 1; + ++found; + + /* Get reference counter */ + { + unsigned int count; + int rc; + + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + skip_eol_spaces( ln ); /* remove spaces at end-of-line */ + + rc = sscanf( ln, "REFERENCE COUNTER: %u", &count ); + if( rc == 1 && cnt != NULL ) + { + *cnt = count; + } + } + } + if( (match = strstr( ln, "REQUIRES:" )) && match == ln ) + { + *stop = ret + 1; + ++found; + } + + ++ret; + } + + free( line ); + + ret = ( found == 2 ) ? 0 : 1; /* 0 - success; 1 - not found. */ + + fseek( log, 0, SEEK_SET ); + } + + return( ret ); +} + +static int get_requires_section( int *start, int *stop, FILE *log ) +{ + int ret = -1, found = 0; + + if( !start || !stop ) return ret; + + if( log != NULL ) + { + char *ln = NULL; + char *line = NULL; + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + ++ret; + *start = 0; *stop = 0; + + while( (ln = fgets( line, PATH_MAX, log )) ) + { + char *match = NULL; + + if( (match = strstr( ln, "REQUIRES:" )) && match == ln ) /* at start of line only */ + { + *start = ret + 1; + ++found; + } + if( (match = strstr( ln, "PACKAGE DESCRIPTION:" )) && match == ln ) + { + *stop = ret + 1; + ++found; + } + + ++ret; + } + + free( line ); + + ret = ( found == 2 ) ? 0 : 1; /* 0 - success; 1 - not found. */ + + fseek( log, 0, SEEK_SET ); + } + + return( ret ); +} + +static int get_description_section( int *start, int *stop, FILE *log ) +{ + int ret = -1, found = 0; + + if( !start || !stop ) return ret; + + if( log != NULL ) + { + char *ln = NULL; + char *line = NULL; + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + ++ret; + *start = 0; *stop = 0; + + while( (ln = fgets( line, PATH_MAX, log )) ) + { + char *match = NULL; + + if( (match = strstr( ln, "PACKAGE DESCRIPTION:" )) && match == ln ) /* at start of line only */ + { + *start = ret + 1; + ++found; + } + if( (match = strstr( ln, "RESTORE LINKS:" )) && match == ln ) + { + *stop = ret + 1; + ++found; + } + + ++ret; + } + + free( line ); + + ret = ( found == 2 ) ? 0 : 1; /* 0 - success; 1 - not found. */ + + fseek( log, 0, SEEK_SET ); + } + + return( ret ); +} + +static int get_restore_links_section( int *start, int *stop, FILE *log ) +{ + int ret = -1, found = 0; + + if( !start || !stop ) return ret; + + if( log != NULL ) + { + char *ln = NULL; + char *line = NULL; + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + ++ret; + *start = 0; *stop = 0; + + while( (ln = fgets( line, PATH_MAX, log )) ) + { + char *match = NULL; + + if( (match = strstr( ln, "RESTORE LINKS:" )) && match == ln ) /* at start of line only */ + { + *start = ret + 1; + ++found; + } + if( (match = strstr( ln, "INSTALL SCRIPT:" )) && match == ln ) + { + *stop = ret + 1; + ++found; + } + + ++ret; + } + + free( line ); + + ret = ( found == 2 ) ? 0 : 1; /* 0 - success; 1 - not found. */ + + fseek( log, 0, SEEK_SET ); + } + + return( ret ); +} + + +static int get_install_script_section( int *start, int *stop, FILE *log ) +{ + int ret = -1, found = 0; + + if( !start || !stop ) return ret; + + if( log != NULL ) + { + char *ln = NULL; + char *line = NULL; + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + ++ret; + *start = 0; *stop = 0; + + while( (ln = fgets( line, PATH_MAX, log )) ) + { + char *match = NULL; + + if( (match = strstr( ln, "INSTALL SCRIPT:" )) && match == ln ) /* at start of line only */ + { + *start = ret + 1; + ++found; + } + if( (match = strstr( ln, "FILE LIST:" )) && match == ln ) + { + *stop = ret + 1; + ++found; + } + + ++ret; + } + + free( line ); + + ret = ( found == 2 ) ? 0 : 1; /* 0 - success; 1 - not found. */ + + fseek( log, 0, SEEK_SET ); + } + + return( ret ); +} + + +static int get_file_list_section( int *start, int *stop, FILE *log ) +{ + int ret = -1, found = 0; + + if( !start || !stop ) return ret; + + if( log != NULL ) + { + char *ln = NULL; + char *line = NULL; + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + ++ret; + *start = 0; *stop = 0; + + while( (ln = fgets( line, PATH_MAX, log )) ) + { + char *match = NULL; + + if( (match = strstr( ln, "FILE LIST:" )) && match == ln ) /* at start of line only */ + { + *start = ret + 1; + ++found; + } + + ++ret; + } + + free( line ); + + ret = ( found == 1 ) ? 0 : 1; /* 0 - success; 1 - not found. */ + + fseek( log, 0, SEEK_SET ); + } + + return( ret ); +} + + +int read_pkginfo( FILE *log, struct package *package ) +{ + int ret = -1; + + char *ln = NULL; + char *line = NULL; + + char *pkgname_pattern = "PACKAGE NAME:", + *pkgver_pattern = "PACKAGE VERSION:", + *arch_pattern = "ARCH:", + *distroname_pattern = "DISTRO:", + *distrover_pattern = "DISTRO VERSION:", + *group_pattern = "GROUP:", + *url_pattern = "URL:", + *license_pattern = "LICENSE:", + *uncompressed_size_pattern = "UNCOMPRESSED SIZE:", + *total_files_pattern = "TOTAL FILES:"; + + + if( !log || !package ) return ret; + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + ++ret; + + while( (ln = fgets( line, PATH_MAX, log )) ) + { + 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_pattern )) && match == ln ) /* at start of line only */ + { + package->pkginfo->name = skip_spaces( ln + strlen( pkgname_pattern ) ); + } + if( (match = strstr( ln, pkgver_pattern )) && match == ln ) + { + package->pkginfo->version = skip_spaces( ln + strlen( pkgver_pattern ) ); + } + if( (match = strstr( ln, arch_pattern )) && match == ln ) + { + package->pkginfo->arch = skip_spaces( ln + strlen( arch_pattern ) ); + } + if( (match = strstr( ln, distroname_pattern )) && match == ln ) + { + package->pkginfo->distro_name = skip_spaces( ln + strlen( distroname_pattern ) ); + } + if( (match = strstr( ln, distrover_pattern )) && match == ln ) + { + package->pkginfo->distro_version = skip_spaces( ln + strlen( distrover_pattern ) ); + } + if( (match = strstr( ln, group_pattern )) && match == ln ) + { + package->pkginfo->group = skip_spaces( ln + strlen( group_pattern ) ); + } + if( (match = strstr( ln, url_pattern )) && match == ln ) + { + package->pkginfo->url = skip_spaces( ln + strlen( url_pattern ) ); + } + if( (match = strstr( ln, license_pattern )) && match == ln ) + { + package->pkginfo->license = skip_spaces( ln + strlen( license_pattern ) ); + } + if( (match = strstr( ln, uncompressed_size_pattern )) && match == ln ) + { + package->pkginfo->uncompressed_size = read_usize( ln + strlen( uncompressed_size_pattern ) ); + } + if( (match = strstr( ln, total_files_pattern )) && match == ln ) + { + package->pkginfo->total_files = read_total_files( ln + strlen( total_files_pattern ) ); + } + + if( (match = strstr( ln, "PACKAGE DESCRIPTION:" )) && match == ln ) + { + char *buf = NULL; + + buf = (char *)malloc( (size_t)PATH_MAX ); + if( !buf ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + /* Get short_description from PACKAGE DESCRIPTION */ + ln = fgets( line, PATH_MAX, log ); + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + + bzero( (void *)buf, PATH_MAX ); + get_short_description( buf, (const char *)line ); + if( buf[0] != '\0' ) + { + package->pkginfo->short_description = xstrdup( (const char *)buf ); + } + free( buf ); + } + + } /* End of while() */ + + free( line ); + + if( package->pkginfo->name == NULL ) ++ret; + if( package->pkginfo->version == NULL ) ++ret; + if( package->pkginfo->arch == NULL ) ++ret; + if( package->pkginfo->distro_name == NULL ) ++ret; + if( package->pkginfo->distro_version == NULL ) ++ret; + /* group can be equal to NULL */ + + fseek( log, 0, SEEK_SET ); + + return( ret ); +} + + +static unsigned int read_references( FILE *log, int start, unsigned int *cnt, struct package *package ) +{ + char *ln = NULL; + char *line = NULL; + char *p = NULL, *group = NULL, *name = NULL, *version = NULL; + int n = 1; + + unsigned int counter, pkgs = 0; + + struct pkg *pkg = NULL; + + if( !log || !cnt || *cnt == 0 || !package ) return pkgs; + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + counter = *cnt; + + while( (ln = fgets( line, PATH_MAX, log )) && (n < start) ) ++n; + + n = 0; + while( (ln = fgets( line, PATH_MAX, log )) ) + { + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + skip_eol_spaces( ln ); /* remove spaces at end-of-line */ + + if( strstr( ln, "REQUIRES:" ) ) break; /* if cnt greater than real number of references */ + + if( n < counter ) + { + if( (p = index( (const char *)ln, '=' )) ) + { + *p = '\0'; version = ++p; + if( (p = index( (const char *)ln, '/' )) ) + { + *p = '\0'; name = ++p; group = (char *)&ln[0]; + } + else + { + name = (char *)&ln[0]; group = NULL; + } + + pkg = pkg_alloc(); + + if( group ) pkg->group = xstrdup( (const char *)group ); + pkg->name = xstrdup( (const char *)name ); + pkg->version = xstrdup( (const char *)version ); + + add_reference( package, pkg ); + ++pkgs; + } + ++n; + } + else + break; + } + + free( line ); + + fseek( log, 0, SEEK_SET ); + + *cnt = pkgs; + + return pkgs; +} + + +static unsigned int read_requires( FILE *log, int start, int stop, struct package *package ) +{ + char *ln = NULL; + char *line = NULL; + char *p = NULL, *group = NULL, *name = NULL, *version = NULL; + int n = 1; + + unsigned int pkgs = 0; + + struct pkg *pkg = NULL; + + if( !log || !package ) return pkgs; + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + while( (ln = fgets( line, PATH_MAX, log )) && (n < start) ) ++n; + + if( start && start < stop ) + { + ++n; /* skip section header */ + + while( (ln = fgets( line, PATH_MAX, log )) ) + { + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + skip_eol_spaces( ln ); /* remove spaces at end-of-line */ + + if( strstr( ln, "PACKAGE DESCRIPTION:" ) ) break; /* if (stop - start - 1) greater than real number of requiress */ + + if( (n > start) && (n < stop) ) + { + if( (p = index( (const char *)ln, '=' )) ) + { + *p = '\0'; version = ++p; + if( (p = index( (const char *)ln, '/' )) ) + { + *p = '\0'; name = ++p; group = (char *)&ln[0]; + } + else + { + name = (char *)&ln[0]; group = NULL; + } + + pkg = pkg_alloc(); + + if( group ) pkg->group = xstrdup( (const char *)group ); + pkg->name = xstrdup( (const char *)name ); + pkg->version = xstrdup( (const char *)version ); + + add_required( package, pkg ); + ++pkgs; + } + + } + ++n; + } + + } /* End if( start && start < stop ) */ + + free( line ); + + fseek( log, 0, SEEK_SET ); + + return pkgs; +} + + +static unsigned int read_description( FILE *log, int start, int stop, struct package *package ) +{ + char *ln = NULL; + char *line = NULL; + char *pattern = NULL; + int n = 1; + + char *tmp_fname = NULL; + FILE *tmp = NULL; + + unsigned int lines = 0; + + if( !log || !package ) return lines; + + tmp_fname = (char *)malloc( (size_t)PATH_MAX ); + if( !tmp_fname ) { FATAL_ERROR( "Cannot allocate memory" ); } + + bzero( (void *)tmp_fname, PATH_MAX ); + (void)sprintf( (char *)&tmp_fname[0], "%s/.DESCRIPTION", tmpdir ); + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) { FATAL_ERROR( "Cannot allocate memory" ); } + + pattern = (char *)malloc( (size_t)strlen( package->pkginfo->name ) + 2 ); + if( !pattern ) { FATAL_ERROR( "Cannot allocate memory" ); } + + (void)sprintf( pattern, "%s:", package->pkginfo->name ); + + + while( (ln = fgets( line, PATH_MAX, log )) && (n < start) ) ++n; + + if( start && start < stop ) + { + ++n; /* skip section header */ + + tmp = fopen( (const char *)&tmp_fname[0], "w" ); + if( !tmp ) + { + FATAL_ERROR( "Cannot create temporary %s file", basename( (char *)&tmp_fname[0] ) ); + } + + while( (ln = fgets( line, PATH_MAX, log )) ) + { + char *match = NULL; + + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + skip_eol_spaces( ln ); /* remove spaces at end-of-line */ + + if( strstr( ln, "RESTORE LINKS:" ) ) break; /* if (stop - start - 1) greater than real number of lines */ + + if( (n > start) && (n < stop) ) + { + /* + skip non-significant spaces at beginning of line + and print lines started with 'pkgname:' + */ + if( (match = strstr( ln, pattern )) && lines < DESCRIPTION_NUMBER_OF_LINES ) + { + int mlen = strlen( match ), plen = strlen( pattern ); + 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 */ + } + fprintf( tmp, "%s\n", match ); + ++lines; + } + + } + ++n; + } + + if( lines < (unsigned int)DESCRIPTION_NUMBER_OF_LINES ) + { + /* WARNING( "Package DESCRIPTION contains less than %d lines", DESCRIPTION_NUMBER_OF_LINES ); */ + while( lines < (unsigned int)DESCRIPTION_NUMBER_OF_LINES ) + { + fprintf( tmp, "%s\n", pattern ); + ++lines; + } + } + + fflush( tmp ); + fclose( tmp ); + + } /* End if( start && start < stop ) */ + + free( pattern ); + free( line ); + + fseek( log, 0, SEEK_SET ); + + /* read temporary saved description */ + { + struct stat sb; + size_t size = 0; + int fd; + + char *desc = NULL; + + if( stat( tmp_fname, &sb ) == -1 ) + { + FATAL_ERROR( "Cannot stat temporary %s file", basename( (char *)&tmp_fname[0] ) ); + } + size = (size_t)sb.st_size; + + if( size ) + { + ssize_t rc = 0; + + desc = (char *)malloc( size + 1 ); + if( !desc ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)desc, size + 1 ); + + if( (fd = open( (const char *)&tmp_fname[0], O_RDONLY )) == -1 ) + { + FATAL_ERROR( "Canot access temporary %s file: %s", basename( (char *)&tmp_fname[0] ), strerror( errno ) ); + } + + rc = read( fd, (void *)desc, size ); + if( rc != (ssize_t)size ) + { + LOG( "ERROR: The %s file is not fully read", basename( (char *)&tmp_fname[0] ) ); + exit_status += 1; + } + + package->description = desc; + + close( fd ); + } + + } + + (void)unlink( tmp_fname ); + free( tmp_fname ); + + return lines; +} + + +static unsigned int read_restore_links( FILE *log, int start, int stop, struct package *package ) +{ + char *ln = NULL; + char *line = NULL; + int n = 1; + + char *tmp_fname = NULL; + FILE *tmp = NULL; + + unsigned int lines = 0; + + if( !log || !package ) return lines; + + tmp_fname = (char *)malloc( (size_t)PATH_MAX ); + if( !tmp_fname ) { FATAL_ERROR( "Cannot allocate memory" ); } + + bzero( (void *)tmp_fname, PATH_MAX ); + (void)sprintf( (char *)&tmp_fname[0], "%s/.RESTORELINKS", tmpdir ); + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) { FATAL_ERROR( "Cannot allocate memory" ); } + + while( (ln = fgets( line, PATH_MAX, log )) && (n < start) ) ++n; + + if( start && start < stop ) + { + ++n; /* skip section header */ + + tmp = fopen( (const char *)&tmp_fname[0], "w" ); + if( !tmp ) + { + FATAL_ERROR( "Cannot create temporary %s file", basename( (char *)&tmp_fname[0] ) ); + } + + while( (ln = fgets( line, PATH_MAX, log )) ) + { + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + skip_eol_spaces( ln ); /* remove spaces at end-of-line */ + + if( strstr( ln, "INSTALL SCRIPT:" ) ) break; /* if (stop - start - 1) greater than real number of lines */ + + if( (n > start) && (n < stop) ) + { + fprintf( tmp, "%s\n", ln ); + ++lines; + } + ++n; + } + + fflush( tmp ); + fclose( tmp ); + + } /* End if( start && start < stop ) */ + + free( line ); + + fseek( log, 0, SEEK_SET ); + + /* read temporary saved description */ + { + struct stat sb; + size_t size = 0; + int fd; + + char *links = NULL; + + if( stat( tmp_fname, &sb ) == -1 ) + { + FATAL_ERROR( "Cannot stat temporary %s file", basename( (char *)&tmp_fname[0] ) ); + } + size = (size_t)sb.st_size; + + if( size ) + { + ssize_t rc = 0; + + links = (char *)malloc( size + 1 ); + if( !links ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)links, size + 1 ); + + if( (fd = open( (const char *)&tmp_fname[0], O_RDONLY )) == -1 ) + { + FATAL_ERROR( "Canot access temporary %s file: %s", basename( (char *)&tmp_fname[0] ), strerror( errno ) ); + } + + rc = read( fd, (void *)links, size ); + if( rc != (ssize_t)size ) + { + LOG( "ERROR: The %s file is not fully read", basename( (char *)&tmp_fname[0] ) ); + exit_status += 1; + } + + package->restore_links = links; + + close( fd ); + } + + } + + (void)unlink( tmp_fname ); + free( tmp_fname ); + + return lines; +} + + +static unsigned int read_install_script( FILE *log, int start, int stop, struct package *package ) +{ + char *ln = NULL; + char *line = NULL; + int n = 1; + + char *tmp_fname = NULL; + FILE *tmp = NULL; + + unsigned int lines = 0; + + if( !log || !package ) return lines; + + tmp_fname = (char *)malloc( (size_t)PATH_MAX ); + if( !tmp_fname ) { FATAL_ERROR( "Cannot allocate memory" ); } + + bzero( (void *)tmp_fname, PATH_MAX ); + (void)sprintf( (char *)&tmp_fname[0], "%s/.INSTALL", tmpdir ); + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) { FATAL_ERROR( "Cannot allocate memory" ); } + + while( (ln = fgets( line, PATH_MAX, log )) && (n < start) ) ++n; + + if( start && start < stop ) + { + ++n; /* skip section header */ + + tmp = fopen( (const char *)&tmp_fname[0], "w" ); + if( !tmp ) + { + FATAL_ERROR( "Cannot create temporary %s file", basename( (char *)&tmp_fname[0] ) ); + } + + while( (ln = fgets( line, PATH_MAX, log )) ) + { + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + skip_eol_spaces( ln ); /* remove spaces at end-of-line */ + + if( strstr( ln, "FILE LIST:" ) ) break; /* if (stop - start - 1) greater than real number of lines */ + + if( (n > start) && (n < stop) ) + { + fprintf( tmp, "%s\n", ln ); + ++lines; + } + ++n; + } + + fflush( tmp ); + fclose( tmp ); + + } /* End if( start && start < stop ) */ + + free( line ); + + fseek( log, 0, SEEK_SET ); + + /* read temporary saved description */ + { + struct stat sb; + size_t size = 0; + int fd; + + char *install = NULL; + + if( stat( tmp_fname, &sb ) == -1 ) + { + FATAL_ERROR( "Cannot stat temporary %s file", basename( (char *)&tmp_fname[0] ) ); + } + size = (size_t)sb.st_size; + + if( size ) + { + ssize_t rc = 0; + + install = (char *)malloc( size + 1 ); + if( !install ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)install, size + 1 ); + + if( (fd = open( (const char *)&tmp_fname[0], O_RDONLY )) == -1 ) + { + FATAL_ERROR( "Canot access temporary %s file: %s", basename( (char *)&tmp_fname[0] ), strerror( errno ) ); + } + + rc = read( fd, (void *)install, size ); + if( rc != (ssize_t)size ) + { + LOG( "ERROR: The %s file is not fully read", basename( (char *)&tmp_fname[0] ) ); + exit_status += 1; + } + + package->install_script = install; + + close( fd ); + } + + } + + (void)unlink( tmp_fname ); + free( tmp_fname ); + + return lines; +} + + +static unsigned int read_file_list( FILE *log, int start, struct package *package ) +{ + char *ln = NULL; + char *line = NULL; + int n = 1; + + unsigned int files = 0; + + if( !log || !package ) return files; + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + while( (ln = fgets( line, PATH_MAX, log )) && (n < start) ) ++n; + + if( start ) + { + while( (ln = fgets( line, PATH_MAX, log )) ) + { + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + skip_eol_spaces( ln ); /* remove spaces at end-of-line */ + + add_file( package, (const char *)ln ); + ++files; + } + + } /* End if( start && start < stop ) */ + + free( line ); + + fseek( log, 0, SEEK_SET ); + + return files; +} + + + +static void _read_pkglog( const char *group, const char *fname ) +{ + FILE *log = NULL; + char *bname = NULL; + + if( fname != NULL ) + { + log = fopen( (const char *)fname, "r" ); + if( !log ) + { + FATAL_ERROR( "Cannot open %s file", fname ); + } + bname = (char *)fname + strlen( tmpdir ) + 1; + } + + if( log != NULL ) + { + struct package *package = NULL; + int rc, start, stop; + unsigned int counter; + + package = package_alloc(); + + if( read_pkginfo( log, package ) != 0 ) + { + LOG( "ERROR: %s: Invalid PKGLOG file", bname ); + exit_status += 1; + package_free( package ); + fclose( log ); + return; + } + + if( hardware ) package->hardware = xstrdup( (const char *)hardware ); + if( tarballs ) /* find tarball and allocate package->tarball */ + { + struct pkginfo *info = package->pkginfo; + const char *tgz = NULL; + char *buf = NULL; + struct stat sb; + + buf = (char *)malloc( (size_t)PATH_MAX ); + if( !buf ) { FATAL_ERROR( "Cannot allocate memory" ); } + + if( info->group ) + { + (void)sprintf( buf, "%s/%s-%s-%s-%s-%s", + info->group, info->name, info->version, info->arch, + info->distro_name, info->distro_version ); + } + else + { + (void)sprintf( buf, "%s-%s-%s-%s-%s", + info->name, info->version, info->arch, + info->distro_name, info->distro_version ); + } + tgz = find_tarball( (const char *)&buf[0] ); + if( tgz ) + { + package->tarball = xstrdup( (const char *)tgz ); + + bzero( (void *)&buf[0], PATH_MAX ); + (void)sprintf( buf, "%s/%s", pkgs_path, tgz ); + if( stat( buf, &sb ) != -1 ) + { + info->compressed_size = (size_t)sb.st_size; + } + } + free( buf ); + } + package->procedure = INSTALL; + package->priority = priority; + + if( package->pkginfo->group && group && strcmp( package->pkginfo->group, group ) != 0 ) + { + char *tgz; + + if( package->tarball ) { tgz = package->tarball; } + else { tgz = basename( (char *)fname ); } + + WARNING( "%s: Should be moved into '%s' subdir", tgz, package->pkginfo->group ); + } + + /****************** + read references: + */ + rc = get_references_section( &start, &stop, &counter, log ); + if( rc != 0 ) + { + LOG( "ERROR: %s: PKGLOG doesn't contains REFERENCE COUNTER section", bname ); + exit_status += 1; + package_free( package ); + fclose( log ); + return; + } + if( counter > 0 ) + { + unsigned int pkgs = counter; + + if( read_references( log, start, &counter, package ) != pkgs ) + { + LOG( "ERROR: %s: Invalid REFERENCE COUNTER section", bname ); + exit_status += 1; + package_free( package ); + fclose( log ); + return; + } + } + + /****************** + read requires: + */ + rc = get_requires_section( &start, &stop, log ); + if( rc != 0 ) + { + LOG( "ERROR: %s: PKGLOG doesn't contains REQUIRES section", bname ); + exit_status += 1; + package_free( package ); + fclose( log ); + return; + } + if( (stop - start) > 1 ) + { + unsigned int pkgs = (unsigned int)(stop - start - 1); /* -1 skips section header */ + + if( read_requires( log, start, stop, package ) != pkgs ) + { + LOG( "ERROR: %s: Invalid REQUIRES section", bname ); + exit_status += 1; + package_free( package ); + fclose( log ); + return; + } + } + + /******************* + read description: + */ + rc = get_description_section( &start, &stop, log ); + if( rc != 0 ) + { + LOG( "ERROR: %s: PKGLOG doesn't contains PACKAGE DESCRIPTION section", bname ); + exit_status += 1; + package_free( package ); + fclose( log ); + return; + } + if( (stop - start) > 1 ) + { + if( read_description( log, start, stop, package ) != (unsigned int)DESCRIPTION_NUMBER_OF_LINES ) + { + LOG( "ERROR: %s: Invalid DESCRIPTION section", bname ); + exit_status += 1; + package_free( package ); + fclose( log ); + return; + } + } + + /********************* + read restore links: + */ + rc = get_restore_links_section( &start, &stop, log ); + if( rc != 0 ) + { + LOG( "ERROR: %s: PKGLOG doesn't contains RESTORE LINKS section", bname ); + exit_status += 1; + package_free( package ); + fclose( log ); + return; + } + if( (stop - start) > 1 ) + { + (void)read_restore_links( log, start, stop, package ); + } + + /********************* + read install script: + */ + rc = get_install_script_section( &start, &stop, log ); + if( rc != 0 ) + { + LOG( "ERROR: %s: PKGLOG doesn't contains INSTALL SCRIPT section", bname ); + exit_status += 1; + package_free( package ); + fclose( log ); + return; + } + if( (stop - start) > 1 ) + { + (void)read_install_script( log, start, stop, package ); + } + + /***************** + read file_list: + */ + rc = get_file_list_section( &start, &stop, log ); + if( rc != 0 ) + { + LOG( "ERROR: %s: PKGLOG doesn't contains FILE LIST section", bname ); + exit_status += 1; + package_free( package ); + fclose( log ); + return; + } + if( start ) + { + unsigned int files = read_file_list( log, start, package ); + if( files == (unsigned int)0 ) + { + /* + Packages that do not contain regular files are ignored. + For example, service package base/init-devices-1.2.3-s9xx-glibc-radix-1.1.txz + */ + if( ! DO_NOT_PRINTOUT_INFO ) + { + LOG( "INFO: %s: PKGLOG contains empty FILE LIST section", bname ); + } + package_free( package ); + fclose( log ); + return; + } + package->pkginfo->total_files = (int)files; + } + + /* + Здесь можно организовать проверку пакета на предмет его + целостности и правильности установки (когда будет готова + утилита check-package). + */ + add_package( package ); + + ++__child; + fclose( log ); + + /*************************************************** + Incremet REFERENCE COUNTERs of required packages: + */ + { + pid_t p = (pid_t) -1; + int rc; + + int len = 0; + char *buf = NULL; + char *cmd = NULL, *errmsg = NULL, *wmsg = NULL; + + struct pkginfo *info = package->pkginfo; + + buf = (char *)malloc( (size_t)PATH_MAX ); + if( !buf ) { FATAL_ERROR( "Cannot allocate memory" ); } + + cmd = (char *)malloc( (size_t)PATH_MAX ); + if( !cmd ) { FATAL_ERROR( "Cannot allocate memory" ); } + + errmsg = (char *)malloc( (size_t)PATH_MAX ); + if( !errmsg ) { FATAL_ERROR( "Cannot allocate memory" ); } + + wmsg = (char *)malloc( (size_t)PATH_MAX ); + if( !wmsg ) { FATAL_ERROR( "Cannot allocate memory" ); } + + if( info->group ) + { + (void)sprintf( &buf[0], "%s/%s-%s-%s-%s-%s", info->group, + info->name, info->version, info->arch, + info->distro_name, info->distro_version ); + } + else + { + (void)sprintf( &buf[0], "%s-%s-%s-%s-%s", + info->name, info->version, info->arch, + info->distro_name, info->distro_version ); + } + + (void)sprintf( &errmsg[0], "Cannot update REFERENCE COUNTERs for '%s' package", buf ); + + len = sprintf( &cmd[0], "%s/chrefs -d %s -o inc %s > /dev/null 2>&1", selfdir, pkgs_path, buf ); + if( len == 0 || len == PATH_MAX - 1 ) + { + FATAL_ERROR( errmsg ); + } + p = sys_exec_command( cmd ); + rc = sys_wait_command( p, (char *)&wmsg[0], PATH_MAX ); + if( rc != 0 ) + { + LOG( "WARNING: %s", errmsg ); + } + + if( buf ) free( buf ); + if( cmd ) free( cmd ); + if( errmsg ) free( errmsg ); + if( wmsg ) free( wmsg ); + } + /* + End of Incremet REFERENCE COUNTERs. + ***************************************************/ + } +} + +static void _read_pkglogs( const char *dirpath, const char *grp ) +{ + DIR *dir; + char *path; + size_t len; + + struct stat path_sb, entry_sb; + struct dirent *entry; + + 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) ) + { + if( check_input_file( NULL, (const char *)path ) == IFMT_LOG ) + { + _read_pkglog( grp, (const char *)path ); + } + } + if( S_ISDIR(entry_sb.st_mode) && grp == NULL ) + { + _read_pkglogs( (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 ); +} + +int read_pkglogs( void ) +{ + int ret = 0; + + __child = 0; + + _read_pkglogs( (const char *)tmpdir, NULL ); + + ret = __child; + + __child = 0; + + return ret; +} + +/*************************************************** + Decremet REFERENCE COUNTERs functions: + */ +static int save_tmp_head( FILE *log, int stop, const char *fname ) +{ + FILE *fp; + int ret = -1; + + char *ln = NULL; + char *line = NULL; + int n = 1, lines = 0; + + if( !stop || !log || !fname || *fname == '\0' ) return ret; + + fp = fopen( fname, "w" ); + if( !fp ) + { + return ret; + } + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + ++ret; + + while( (ln = fgets( line, PATH_MAX, log )) ) + { + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + skip_eol_spaces( ln ); /* remove spaces at end-of-line */ + + if( n < stop ) + { + fprintf( fp, "%s\n", ln ); + ++n; ++lines; + } + else + break; + } + + ret = lines; /* number of lines in the HEAD */ + + free( line ); + + fseek( log, 0, SEEK_SET ); + fclose( fp ); + + return ret; +} + +static int save_tmp_tail( FILE *log, int start, const char *fname ) +{ + FILE *fp; + int ret = -1; + + char *ln = NULL; + char *line = NULL; + int n = 1, lines = 0; + + if( !start || !log || !fname || *fname == '\0' ) return ret; + + fp = fopen( fname, "w" ); + if( !fp ) + { + return ret; + } + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + ++ret; + + while( (ln = fgets( line, PATH_MAX, log )) && (n < start) ) ++n; + + while( (ln = fgets( line, PATH_MAX, log )) ) + { + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + skip_eol_spaces( ln ); /* remove spaces at end-of-line */ + + fprintf( fp, "%s\n", ln ); + ++lines; + } + + ret = lines; /* number of lines in the TAIL */ + + free( line ); + + fseek( log, 0, SEEK_SET ); + fclose( fp ); + + return ret; +} + +static int write_tmp_part( FILE *log, const char *fname ) +{ + FILE *fp; + int ret = -1; + + char *ln = NULL; + char *line = NULL; + int lines = 0; + + if( !log || !fname || *fname == '\0' ) return ret; + + fp = fopen( fname, "r" ); + if( !fp ) + { + return ret; + } + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + ++ret; + + 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 */ + + fprintf( log, "%s\n", ln ); + ++lines; + } + + ret = lines; /* number of written lines */ + + free( line ); + + fclose( fp ); + + return ret; +} + + + +static char **create_references( size_t size ) +{ + char **references = (char **)0; + + if( size > 0 ) + { + references = (char **)malloc( size * sizeof(char *) ); + bzero( (void *)references, size * sizeof(char *) ); + } + + return( references ); +} + +static void free_references( char **references ) +{ + if( references ) + { + char **ptr = references; + + while( *ptr ) + { + if( *ptr ) free( *ptr ); + ptr++; + } + free( references ); + } +} + + +static char **get_references( FILE *log, int start, unsigned int *cnt, char *grp, char *name, char *version ) +{ + char **refs = (char **)0; + char **ptr; + + char *ln = NULL; + char *line = NULL; + int n = 1; + + size_t len = 0; + + unsigned int counter, pkgs; + + char *pkg = NULL; + + if( !log || !cnt || *cnt == 0 || !name || !version ) return refs; + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + pkg = (char *)malloc( (size_t)PATH_MAX ); + if( !pkg ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + counter = *cnt; + + if( grp && *grp != '\0' ) { (void)sprintf( pkg, "%s/%s=", grp, name ); } + else { (void)sprintf( pkg, "%s=", name ); } + + len = strlen( pkg ); + + refs = ptr = create_references( counter + 1 ); /* null terminated char *references[] */ + + while( (ln = fgets( line, PATH_MAX, log )) && (n < start) ) ++n; + + n = 0; pkgs = 0; + while( (ln = fgets( line, PATH_MAX, log )) ) + { + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + skip_eol_spaces( ln ); /* remove spaces at end-of-line */ + + if( strstr( ln, "REQUIRES:" ) ) break; /* if cnt greater than real number of references */ + + if( n < counter ) + { + if( strncmp( ln, pkg, len ) ) /* always remove 'name=version' from list */ + { + if( refs ) + { + *ptr = xstrdup( (const char *)ln ); ++ptr; + *ptr = (char *)0; + ++pkgs; + } + } + ++n; + } + else + break; + } + + free( line ); free( pkg ); + + fseek( log, 0, SEEK_SET ); + + if( pkgs == 0 ) + { + free_references( refs ); + refs = (char **)0; + } + + *cnt = pkgs; + + return refs; +} + +static void _change_references( char *grp, char *name, char *version, const char *log_fname ) +{ + int fd; + FILE *log; + + char uncompress = '\0'; + + char *head_fname = NULL, *tail_fname = NULL; + int head_lines, tail_lines; + + int rc, start, stop; + unsigned int counter; + + int inc = 0; + + char **references = NULL; + + char *bname = (char *)log_fname + strlen( pkgs_path ) + 1; + + if( !name || !version || log_fname == NULL ) return; + if( check_input_file( &uncompress, log_fname ) != IFMT_LOG ) return; + + log = fopen( (const char *)log_fname, "r+" ); + if( !log ) + { + LOG( "ERROR: Cannot access %s file: %s", bname, strerror( errno ) ); + exit_status += 1; + return; + } + + fd = __lock_file( log ); + + rc = get_references_section( &start, &stop, &counter, log ); + if( rc != 0 ) + { + LOG( "ERROR: %s: PKGLOG doesn't contains REFERENCE COUNTER section", bname ); + exit_status += 1; + __unlock_file( fd ); fclose( log ); + return; + } + + head_fname = (char *)alloca( strlen( tmpdir ) + 7 ); + (void)sprintf( head_fname, "%s/.HEAD", tmpdir ); + + tail_fname = (char *)alloca( strlen( tmpdir ) + 7 ); + (void)sprintf( tail_fname, "%s/.TAIL", tmpdir ); + + head_lines = save_tmp_head( log, start, (const char *)head_fname ); + tail_lines = save_tmp_tail( log, stop - 1, (const char *)tail_fname ); + + if( head_lines < 10 && tail_lines < 12 ) + { + LOG( "ERROR: %s: Invalid PKGLOG file", bname ); + exit_status += 1; + __unlock_file( fd ); fclose( log ); + return; + } + + references = get_references( log, start, &counter, grp, name, version ); + + if( ftruncate( fd, 0 ) != 0 ) + { + LOG( "ERROR: Cannot change REFERENCE COUNTER in the %s file: %s", bname, strerror( errno ) ); + exit_status += 1; + free_references( references ); + __unlock_file( fd ); fclose( log ); + return; + } + + head_lines = write_tmp_part( log, (const char *)head_fname ); + + if( inc ) ++counter; + fprintf( log, "REFERENCE COUNTER: %u\n", counter ); + if( inc ) + { + if( grp && *grp != '\0' ) + { + fprintf( log, "%s/%s=%s\n", grp, name, version ); + } + else + { + fprintf( log, "%s=%s\n", name, version ); + } + } + + if( references ) + { + char **ptr = references; + + while( *ptr ) + { + if( *ptr ) fprintf( log, "%s\n", *ptr ); + ptr++; + } + + free_references( references ); + } + + tail_lines = write_tmp_part( log, (const char *)tail_fname ); + + __unlock_file( fd ); + fclose( log ); +} + +static void _decrement_references( void *data, void *user_data ) +{ + struct pkg *pkg = (struct pkg *)data; + const char *fname = (const char *)user_data; + + if( pkg && fname ) + { + _change_references( pkg->group, pkg->name, pkg->version, fname ); + } +} + +static void decrement_references( const char *fname ) +{ + dlist_foreach( extern_requires, _decrement_references, (void *)fname ); +} + +static void _decrement_reference_counters( void *data, void *user_data ) +{ + struct package *package = (struct package *)data; + + if( package && package->pkginfo ) + { + char *buf = NULL; + struct pkginfo *info = package->pkginfo; + + buf = (char *)malloc( (size_t)PATH_MAX ); + if( !buf ) { FATAL_ERROR( "Cannot allocate memory" ); } + + if( info->group ) + { + (void)sprintf( &buf[0], "%s/%s/%s-%s-%s-%s-%s", pkgs_path, + info->group, + info->name, info->version, info->arch, + info->distro_name, info->distro_version ); + } + else + { + (void)sprintf( &buf[0], "%s/%s-%s-%s-%s-%s", pkgs_path, + info->name, info->version, info->arch, + info->distro_name, info->distro_version ); + } + + decrement_references( (const char *)&buf[0] ); + + free( buf ); + } +} + +static void decrement_reference_counters( void ) +{ + dlist_foreach( provides, _decrement_reference_counters, NULL ); +} + +/**************************************************************** + Если после апдейта пакет просто сменил группу, то его надо + удалить из списка внешних зависимостей, ведь он предоставляет + нужную функциональность. + + Например: libs/libspectre требует libs/cairo, а xlibs/cairo + требует libs/libspectre, однако libs/libspectre может + использовать как libs/cairo так и xlibs/cairo . + + Search installed package with specified name within any group: + */ +static char *pkglog_fname = NULL; + +static void _probe_pkglog( const char *pname, 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, pname )) && match == pkglog ) + { + char *buf = NULL, *p = NULL, *q = NULL; + + p = q = buf = xstrdup( (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(pname) >= strlen(buf) ) + { + + pkglog_fname = xstrdup( (const char *)path ); + free( buf ); + closedir( dir ); + return; + } + free( buf ); + } + } + if( S_ISDIR(entry_sb.st_mode) && grp == NULL ) + { + _probe_pkglog( pname, (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( const char *name ) +{ + char *ret = NULL; + + _probe_pkglog( name, (const char *)pkgs_path, NULL ); + if( pkglog_fname ) + { + ret = pkglog_fname; + } + + return ret; +} + +static int check_installed_pkgname( const char *name ) +{ + int ret = 0; + char *fname = NULL; + + if( !name ) return ret; + + fname = probe_package( name ); + if( fname ) + { + ret = 1; + free( pkglog_fname ); + pkglog_fname = NULL; + } + + return ret; +} +/* + End of search installed package. + ****************************************************************/ + +static int logged_out = 0; + +static void _log_extern_requires( void *data, void *user_data ) +{ + struct pkg *pkg = (struct pkg *)data; + + if( pkg && !check_installed_pkgname( (const char *)pkg->name ) ) + { + if( !logged_out ) + { + /* LOG( "Система требует инсталляции следующих пакетов:" ); */ + LOG( "The System requires following packages:" ); + ++logged_out; + } + if( pkg->group ) + LOG( " %s/%s-%s : have to be %s", pkg->group, pkg->name, pkg->version, strproc( pkg->procedure ) ); + else + LOG( " %s-%s : have to be %s", pkg->name, pkg->version, strproc( pkg->procedure ) ); + } +} + +static void log_extern_requires( void ) +{ + dlist_foreach( extern_requires, _log_extern_requires, NULL ); + + if( logged_out ) { LOG( "End of requires list." ); } +} +/* + End of decremet REFERENCE COUNTERs functions. + ***************************************************/ + +/*************************************************** + Remove empty DB directories: + */ +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_empty_dirs( const char *dirpath ) +{ + DIR *dir; + char *path; + size_t len; + + struct stat path_sb, entry_sb; + struct dirent *entry; + + char *pname = (char *)dirpath + (( root ) ? strlen( root ) : 0) ; /* 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_ISDIR(entry_sb.st_mode) ) + { + if( is_dir_empty( (const char *)path ) ) + { + (void)rmdir( (const char *)path ); + } + } + } + /* else { stat() returns error code; errno is set; and we have to continue the loop } */ + } + + closedir( dir ); +} + +static void remove_empty_dirs( const char *path ) +{ + char *buf = NULL; + + buf = (char *)malloc( (size_t)PATH_MAX ); + if( !buf ) { FATAL_ERROR( "Cannot allocate memory" ); } + bzero( (void *)buf, PATH_MAX ); + + if( root ) + { + int len = strlen( root ); + + (void)strcpy( buf, (const char *)root ); + if( buf[ len - 1 ] != '/' ) + { + buf[len] = '/'; buf[len+1] = '\0'; + } + (void)strcat( buf, (const char *)path ); + } + else + { + (void)strcpy( buf, path ); + } + + _remove_empty_dirs( (const char *)&buf[0] ); + + free( buf ); +} +/* + End of remove empty DB directories. + ***************************************************/ + + +/********************************************* + Get directory where this program is placed: + */ +char *get_selfdir( void ) +{ + char *buf = NULL; + ssize_t len; + + buf = (char *)malloc( PATH_MAX ); + if( !buf ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + bzero( (void *)buf, PATH_MAX ); + len = readlink( "/proc/self/exe", buf, (size_t)PATH_MAX ); + if( len > 0 && len < PATH_MAX ) + { + char *p = xstrdup( (const char *)dirname( buf ) ); + free( buf ); + return p; + } + FATAL_ERROR( "Cannot determine self directory. Please mount /proc filesystem" ); +} + +void set_stack_size( void ) +{ + const rlim_t stack_size = 16 * 1024 * 1024; /* min stack size = 16 MB */ + struct rlimit rl; + int ret; + + ret = getrlimit( RLIMIT_STACK, &rl ); + if( ret == 0 ) + { + if( rl.rlim_cur < stack_size ) + { + rl.rlim_cur = stack_size; + ret = setrlimit( RLIMIT_STACK, &rl ); + if( ret != 0 ) + { + fprintf(stderr, "setrlimit returned result = %d\n", ret); + FATAL_ERROR( "Cannot set stack size" ); + } + } + } +} + +static void open_errlog_file( void ) +{ + if( errlog_fname && (strcmp( errlog_fname, "-" ) != 0) ) + { + errlog = fopen( (const char *)errlog_fname, "w" ); + if( !errlog ) + { + FATAL_ERROR( "Cannot create LOG '%s' file", basename( errlog_fname ) ); + } + close_log_file = 1; + } + else + { + errlog = stderr; + close_log_file = 0; + } +} + + +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(); + + errlog = stderr; + + program = basename( argv[0] ); + get_args( argc, argv ); + + /* set_stack_size(); */ + + open_errlog_file(); + + tmpdir = _mk_tmpdir(); + if( !tmpdir ) + { + FATAL_ERROR( "Cannot create temporary directory" ); + } + + LOG( "Check Setup Database Integrity:" ); + + /* Copy PKGLOGs into TMPDIR: */ + { + int pkgs = copy_pkglogs(); + if( pkgs == 0 ) { FATAL_ERROR( "There are no PKGLOG files in the '%s' directory", pkgs_path ); } + if( exit_status > 0 ) { FATAL_ERROR( "Cannot copy some PKGLOG file" ); } + if( ! DO_NOT_PRINTOUT_INFO ) + { + INFO( "Found %d PKGLOG files in the '%s' directory", pkgs, pkgs_path ); + } + } + + /* Read PKGLOGs from TMPDIR and create Double Linked List of PACKAGES: */ + { + int pkgs = read_pkglogs(); + if( pkgs == 0 ) { FATAL_ERROR( "There are no PKGLOG files in the '%s' directory", tmpdir ); } + if( exit_status > 0 ) { FATAL_ERROR( "Cannot read some PKGLOG file" ); } + if( ! DO_NOT_PRINTOUT_INFO ) + { + /* INFO( "Found %d PKGLOG files in the '%s' directory", pkgs, tmpdir ); */ + } + } + + + { + int extern_pkgs = create_provides_list( NULL ); + + /* + На данном этапе, для каждого пакета, добавленного в список + packages, уже выполнена операция 'chrefs -d pkgs_path -o inc PKGLOG' + остается только обработать внешние зависимости, если таковые есть. + */ + if( extern_pkgs ) + { + /* + 1. для каждого элемента сиска provides проходим по extern_requires и делаем + crefs dec (функцию надо взять из chrefs.c так как в списке extern_requires + структуры имеют только 3 поля: group, name, version) + */ + decrement_reference_counters(); + + /* + 2. напечатать список требуемых пакетов: + */ + log_extern_requires(); + } + + free_provides_list(); + } + + + if( exit_status == 0 ) + { + LOG( "Setup Database is clean." ); + } + else + { + LOG( "Setup Database is not fully clean. See the LOG mesages above." ); + } + + if( root ) + { + remove_empty_dirs( PACKAGES_PATH ); + remove_empty_dirs( REMOVED_PKGS_PATH ); + } + else + { + remove_empty_dirs( (const char *)pkgs_path ); + } + + if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); } + free_resources(); + + exit( exit_status ); +} |