diff options
Diffstat (limited to 'src/chrefs.c')
-rw-r--r-- | src/chrefs.c | 1933 |
1 files changed, 1933 insertions, 0 deletions
diff --git a/src/chrefs.c b/src/chrefs.c new file mode 100644 index 0000000..8820aea --- /dev/null +++ b/src/chrefs.c @@ -0,0 +1,1933 @@ + +/********************************************************************** + + 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 <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> + +#define PROGRAM_NAME "chrefs" + +#include <defs.h> + + +char *program = PROGRAM_NAME; +char *destination = NULL, *operation = NULL, *pkglog_fname = NULL, + *tmpdir = NULL, *requires_fname = NULL; + +int exit_status = EXIT_SUCCESS; /* errors counter */ +char *selfdir = NULL; + +char *pkgname = NULL, + *pkgver = NULL, + *arch = NULL, + *distroname = NULL, + *distrover = NULL, + *group = NULL; + + +/******************************************** + *requires[] declarations: + */ +struct package { + char *name; + char *version; + char *arch; + char *distro_name; + char *distro_version; +}; + +static struct package *create_package( char *name, char *version, + char *arch, char *distro_name, + char *distro_version ); +static void free_package( struct package *pkg ); +static struct package **create_requires( size_t size ); +static void free_requires( struct package **requires ); +/* + End of *requires[] declarations. + ********************************************/ + +struct package **requires = NULL; + + +/******************************************** + LOCK FILE declarations: + */ +static int __lock_file( FILE *fp ); +static void __unlock_file( int fd ); +/* + End of LOCK FILE declarations. + ********************************************/ + + +#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( requires ) { free_requires( requires ); } requires = NULL + +void free_resources() +{ + if( selfdir ) { free( selfdir ); selfdir = NULL; } + if( destination ) { free( destination ); destination = NULL; } + if( operation ) { free( operation ); operation = NULL; } + if( pkglog_fname ) { free( pkglog_fname ); pkglog_fname = NULL; } + if( requires_fname ) { free( requires_fname ); requires_fname = NULL; } + + FREE_PKGINFO_VARIABLES(); +} + +void usage() +{ + free_resources(); + + fprintf( stdout, "\n" ); + fprintf( stdout, "Usage: %s [options] <pkglog>\n", program ); + fprintf( stdout, "\n" ); + fprintf( stdout, "Increment or Decrement reference counters in required PKGLOG files\n" ); + fprintf( stdout, "in the Setup Database packages directory, which is determined by\n" ); + fprintf( stdout, "option --destination OR by the directory where the input <pkglog>\n" ); + fprintf( stdout, "file is located. If destination is defined then <pkglog> file name\n" ); + fprintf( stdout, "should be defined relative to destination.\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, " -d,--destination=<DIR> Setup Database packages directory.\n" ); + fprintf( stdout, " -o,--operation=<inc|dec> Operation: Increment or Decrement.\n" ); + fprintf( stdout, "\n" ); + fprintf( stdout, "Parameter:\n" ); + fprintf( stdout, " <pkglog> Input PKGLOG file (TEXT format).\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; +} + + +/******************************************** + *requires[] functions: + */ +static struct package *create_package( char *name, char *version, + char *arch, char *distro_name, + char *distro_version ) +{ + struct package *pkg = (struct package *)0; + + if( !name || *name == '\0' ) return pkg; + if( !version || *version == '\0' ) return pkg; + if( !arch || *arch == '\0' ) return pkg; + if( !distro_name || *distro_name == '\0' ) return pkg; + if( !distro_version || *distro_version == '\0' ) return pkg; + + pkg = (struct package *)malloc( sizeof(struct package) ); + if( pkg ) + { + pkg->name = xstrdup( (const char *)name ); + pkg->version = xstrdup( (const char *)version ); + pkg->arch = xstrdup( (const char *)arch ); + pkg->distro_name = xstrdup( (const char *)distro_name ); + pkg->distro_version = xstrdup( (const char *)distro_version ); + } + + return pkg; +} + +static void free_package( struct package *pkg ) +{ + if( pkg ) + { + if( pkg->name ) free( pkg->name ); + if( pkg->version ) free( pkg->version ); + if( pkg->arch ) free( pkg->arch ); + if( pkg->distro_name ) free( pkg->distro_name ); + if( pkg->distro_version ) free( pkg->distro_version ); + + free( pkg ); + } +} + +static struct package **create_requires( size_t size ) +{ + struct package **requires = (struct package **)0; + + if( size > 0 ) + { + requires = (struct package **)malloc( size * sizeof(struct package *) ); + bzero( (void *)requires, size * sizeof(struct package *) ); + } + + return( requires ); +} + +static void free_requires( struct package **requires ) +{ + if( requires ) + { + struct package **ptr = requires; + + while( *ptr ) + { + if( *ptr ) free_package( *ptr ); + ptr++; + } + free( requires ); + } +} +/* + End of *requires[] functions. + ********************************************/ + + +/******************************************** + 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(); +} + +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 ); +} + +enum _pkglog_type +{ + PKGLOG_TEXT = 0, + PKGLOG_GZ, + PKGLOG_BZ2, + PKGLOG_XZ, + PKGLOG_TAR, + + PKGLOG_UNKNOWN +}; + +static enum _pkglog_type pkglog_type = PKGLOG_UNKNOWN; +static char uncompress[2] = { 0, 0 }; + + +static enum _pkglog_type check_pkglog_file( 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 */ + + uncompress[0] = '\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 ) + { + FATAL_ERROR( "Unknown type of input file %s", basename( (char *)fname ) ); + } + buf[7] = '\0'; + + /* TEXT */ + if( !strncmp( (const char *)&buf[0], "PACKAGE", 7 ) ) + { + close( fd ); return PKGLOG_TEXT; + } + + /* GZ */ + if( buf[0] == 0x1F && buf[1] == 0x8B && buf[2] == 0x08 ) + { + uncompress[0] = 'x'; + close( fd ); return PKGLOG_GZ; + } + + /* BZ2 */ + if( buf[0] == 0x42 && buf[1] == 0x5A && buf[2] == 0x68 ) + { + uncompress[0] = 'j'; + close( fd ); return PKGLOG_BZ2; + } + + /* XZ */ + if( buf[0] == 0xFD && buf[1] == 0x37 && buf[2] == 0x7A && + buf[3] == 0x58 && buf[4] == 0x5A && buf[5] == 0x00 ) + { + uncompress[0] = 'J'; + close( fd ); return PKGLOG_XZ; + } + + 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 PKGLOG_TAR; + } + } + + close( fd ); return PKGLOG_UNKNOWN; +} + + +void get_args( int argc, char *argv[] ) +{ + const char* short_options = "hvd:o:"; + + const struct option long_options[] = + { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'v' }, + { "destination", required_argument, NULL, 'd' }, + { "operation", required_argument, NULL, 'o' }, + { 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 'd': + { + if( optarg != NULL ) + { + destination = xstrdup( (const char *)optarg ); + remove_trailing_slash( destination ); + } + else + /* option is present but without value */ + usage(); + break; + } + case 'o': + { + operation = xstrdup( (const char *)optarg ); + to_lowercase( operation ); + if( strcmp( operation, "inc" ) != 0 && strcmp( operation, "dec" ) != 0 ) + { + ERROR( "Invalid '%s' operation requested", operation ); + usage(); + } + break; + } + + case '?': default: + { + usage(); + break; + } + } + } + + + if( operation == NULL ) + { + usage(); + } + + /* last command line argument is the LOGFILE */ + if( optind < argc ) + { + char *buf = NULL; + + buf = (char *)malloc( (size_t)PATH_MAX ); + if( !buf ) { FATAL_ERROR( "Cannot allocate memory" ); } + + if( destination == NULL ) + { + pkglog_fname = xstrdup( (const char *)argv[optind++] ); + if( pkglog_fname == NULL ) + { + FATAL_ERROR( "Unable to set input PKGLOG file name" ); + } + + bzero( (void *)buf, PATH_MAX ); + (void)sprintf( buf, "%s", pkglog_fname ); + destination = xstrdup( (const char *)dirname( buf ) ); + if( destination == NULL ) + { + FATAL_ERROR( "Unable to set destination directory" ); + } + + } + else + { + bzero( (void *)buf, PATH_MAX ); + (void)sprintf( buf, "%s/%s", destination, argv[optind++] ); + pkglog_fname = xstrdup( (const char *)buf ); + if( pkglog_fname == NULL ) + { + FATAL_ERROR( "Unable to set inpit PKGLOG file name" ); + } + } + + free( buf ); + + pkglog_type = check_pkglog_file( (const char *)pkglog_fname ); + if( pkglog_type != PKGLOG_TEXT ) + { + ERROR( "%s: Unknown input file format", basename( pkglog_fname ) ); + usage(); + } + + } + else + { + usage(); + } +} + + +/* + Especialy for pkginfo lines. + Remove leading spaces and take non-space characters only: + */ +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'; } +} + + + +int get_pkginfo() +{ + int ret = -1; + FILE *log = NULL; + + if( pkglog_fname != NULL ) + { + log = fopen( (const char *)pkglog_fname, "r" ); + if( !log ) + { + FATAL_ERROR( "Cannot open %s file", pkglog_fname ); + } + } + + if( log != NULL ) + { + char *ln = NULL; + char *line = NULL; + + char *pkgname_pattern = "PACKAGE NAME:", + *pkgver_pattern = "PACKAGE VERSION:", + *group_pattern = "GROUP:", + *arch_pattern = "ARCH:", + *distroname_pattern = "DISTRO:", + *distrover_pattern = "DISTRO VERSION:"; + + int last = 10; /* read first 10 lines only */ + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + ++ret; + + if( last ) + { + int n = 1; + + 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 */ + { + pkgname = skip_spaces( ln + strlen( pkgname_pattern ) ); + } + if( (match = strstr( ln, pkgver_pattern )) && match == ln ) + { + pkgver = skip_spaces( ln + strlen( pkgver_pattern ) ); + } + if( (match = strstr( ln, arch_pattern )) && match == ln ) + { + arch = skip_spaces( ln + strlen( arch_pattern ) ); + } + if( (match = strstr( ln, distroname_pattern )) && match == ln ) + { + distroname = skip_spaces( ln + strlen( distroname_pattern ) ); + } + if( (match = strstr( ln, distrover_pattern )) && match == ln ) + { + distrover = skip_spaces( ln + strlen( distrover_pattern ) ); + } + if( (match = strstr( ln, group_pattern )) && match == ln ) + { + group = skip_spaces( ln + strlen( group_pattern ) ); + } + + if( n < last ) ++n; + else break; + + } /* End of while() */ + + } + + free( line ); + + if( pkgname == NULL ) ++ret; + if( pkgver == NULL ) ++ret; + if( arch == NULL ) ++ret; + if( distroname == NULL ) ++ret; + if( distrover == NULL ) ++ret; + /* group can be equal to NULL */ + + fclose( log ); + } + + return( ret ); +} + + + +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; + + 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 ); +} + + + +int get_requires_section( int *start, int *stop, const char *log_fname ) +{ + int ret = -1, found = 0; + + FILE *log = NULL; + + if( !start || !stop ) return ret; + + if( log_fname != NULL ) + { + log = fopen( (const char *)log_fname, "r" ); + if( !log ) + { + 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; + + 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. */ + + fclose( log ); + } + + return( ret ); +} + + +int get_description_section( int *start, int *stop, const char *log_fname ) +{ + int ret = -1, found = 0; + + FILE *log = NULL; + + if( !start || !stop ) return ret; + + if( log_fname != NULL ) + { + log = fopen( (const char *)log_fname, "r" ); + if( !log ) + { + 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; + + 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. */ + + fclose( log ); + } + + return( ret ); +} + + +int get_restore_links_section( int *start, int *stop, const char *log_fname ) +{ + int ret = -1, found = 0; + + FILE *log = NULL; + + if( !start || !stop ) return ret; + + if( log_fname != NULL ) + { + log = fopen( (const char *)log_fname, "r" ); + if( !log ) + { + 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; + + 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. */ + + fclose( log ); + } + + return( ret ); +} + + +int get_install_script_section( int *start, int *stop, const char *log_fname ) +{ + int ret = -1, found = 0; + + FILE *log = NULL; + + if( !start || !stop ) return ret; + + if( log_fname != NULL ) + { + log = fopen( (const char *)log_fname, "r" ); + if( !log ) + { + 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; + + 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. */ + + fclose( log ); + } + + return( ret ); +} + + +int get_file_list_section( int *start, int *stop, const char *log_fname ) +{ + int ret = -1, found = 0; + + FILE *log = NULL; + + if( !start || !stop ) return ret; + + if( log_fname != NULL ) + { + log = fopen( (const char *)log_fname, "r" ); + if( !log ) + { + 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; + *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. */ + + fclose( log ); + } + + return( ret ); +} + + + + +/*********************************************************** + get_requires(): + -------------- + if( success ) - returns number of requires + if( error ) - returns -1 + */ +int get_requires( const char *requires, const char *log_fname ) +{ + int ret = -1; + FILE *req = NULL, *log = NULL; + + int start = 0, stop = 0; + + if( get_requires_section( &start, &stop, log_fname ) != 0 ) + { + return ret; + } + + if( log_fname != NULL ) + { + log = fopen( (const char *)log_fname, "r" ); + if( ! log ) + { + return ret; + } + } + + if( requires != NULL ) + { + req = fopen( (const char *)requires, "w" ); + if( ! req ) + { + return ret; + } + } + + /* get PKGLOG sections */ + { + char *ln = NULL; + char *line = NULL; + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + ++ret; + + if( start && start < stop ) + { + int n = 1, lines = 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( (n > start) && (n < stop) ) + { + fprintf( req, "%s\n", ln ); + ++lines; + } + ++n; + } + + ret = lines; /* number of lines in the LIST */ + } + + free( line ); + + fclose( log ); + fflush( req ); fclose( req ); + } + + return( ret ); +} + + +struct package **read_requires( const char *fname, int n ) +{ + struct package **requires = (struct package **)0; + struct package **ptr = (struct package **)0; + + FILE *file; + + if( ! fname ) return NULL; + + requires = create_requires( n + 1 ); + if( requires ) + { + int i; + char *ln = NULL; + char *line = NULL; + + line = (char *)malloc( (size_t)PATH_MAX ); + if( !line ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + struct package *package; + + file = fopen( fname, "r" ); + if( !file ) + { + free( line ); + free_requires( requires ); + return NULL; + } + + ptr = requires; + for( i = 0; i < n; ++i ) + { + if( (ln = fgets( line, PATH_MAX, file )) ) + { + char *d, *name, *version; + + ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */ + skip_eol_spaces( ln ); /* remove spaces at end-of-line */ + + /* сut 'name=version' by delimiter '=': */ + d = index( ln, '=' ); *d = '\0'; + version = ++d; name = ln; + + package = create_package( name, version, arch, DISTRO_NAME, DISTRO_VERSION ); + if( package ) + { + *ptr = package; + ptr++; + } + } + } + *ptr = (struct package *)0; + + free( line ); + } + + return requires; +} + +int find_requires( char *pname, const char *grp ) +{ + char *name = NULL; + char *buf = NULL; + + struct package **ptr = requires; + + buf = (char *)malloc( (size_t)PATH_MAX ); + + if( !buf ) { FATAL_ERROR( "Cannot allocate memory" ); } + + while( *ptr ) + { + if( grp && *grp != '\0' ) + { + char *p = NULL; + size_t len = strlen( (*ptr)->name ); + + if( (p = strstr( (*ptr)->name, grp )) && (p == (*ptr)->name) ) + { + /* if group is equal to required package's group then remove group from the name */ + name = alloca( len + 1 ); + strcpy( name, (const char *)(*ptr)->name ); + + name = index( name, '/' ) + 1; + } + else + { + /* if group is not equal to required package's group then add group to the name */ + name = alloca( len + strlen( grp ) + 2 ); + (void)sprintf( name, "%s/%s", grp, (const char *)(*ptr)->name ); + } + } + else + { + name = (*ptr)->name; + } + + (void)sprintf( buf, "%s-%s-%s-%s-%s", + name, (*ptr)->version, (*ptr)->arch, + (*ptr)->distro_name, (*ptr)->distro_version ); + + if( ! strcmp( buf, pname ) ) + { + free( buf ); return 1; + } + ptr++; + } + + free( buf ); + + return 0; +} + + +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; +} + +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; +} + +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 *head_fname = NULL, *tail_fname = NULL; + int head_lines, tail_lines; + + int rc, start, stop; + unsigned int counter; + + int inc = ( strcmp( operation, "inc" ) == 0 ) ? 1 : 0; + + char **references = NULL; + + if( !name || !version || log_fname == NULL ) return; + if( check_pkglog_file( log_fname ) != PKGLOG_TEXT ) return; + + log = fopen( (const char *)log_fname, "r+" ); + if( !log ) + { + ERROR( "Cannot access %s file: %s", log_fname, strerror( errno ) ); + return; + } + + fd = __lock_file( log ); + + rc = get_references_section( &start, &stop, &counter, log ); + if( rc != 0 ) + { + ERROR( "%s: PKGLOG doesn't contains REFERENCE COUNTER section", log_fname ); + __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 ) + { + ERROR( "%s: Invalid PKGLOG file", log_fname ); + __unlock_file( fd ); fclose( log ); + return; + } + + references = get_references( log, start, &counter, grp, name, version ); + + if( ftruncate( fd, 0 ) != 0 ) + { + ERROR( "Cannot change REFERENCE COUNTER in the %s file: %s", log_fname, strerror( errno ) ); + 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 _search_required_packages( 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( find_requires( entry->d_name, grp ) ) + { + _change_references( group, pkgname, pkgver, (const char *)path ); + } + } + if( S_ISDIR(entry_sb.st_mode) && grp == NULL ) + { + _search_required_packages( (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 ); +} + + + + +/********************************************* + Get directory where this program is placed: + */ +char *get_selfdir( void ) +{ + char *buf = NULL; + ssize_t len; + + buf = (char *)malloc( PATH_MAX ); + if( !buf ) + { + FATAL_ERROR( "Cannot allocate memory" ); + } + + bzero( (void *)buf, PATH_MAX ); + len = readlink( "/proc/self/exe", buf, (size_t)PATH_MAX ); + if( len > 0 && len < PATH_MAX ) + { + char *p = xstrdup( (const char *)dirname( buf ) ); + free( buf ); + return p; + } + FATAL_ERROR( "Cannot determine self directory. Please mount /proc filesystem" ); +} + +void set_stack_size( void ) +{ + const rlim_t stack_size = 16 * 1024 * 1024; /* min stack size = 16 MB */ + struct rlimit rl; + int ret; + + ret = getrlimit( RLIMIT_STACK, &rl ); + if( ret == 0 ) + { + if( rl.rlim_cur < stack_size ) + { + rl.rlim_cur = stack_size; + ret = setrlimit( RLIMIT_STACK, &rl ); + if( ret != 0 ) + { + fprintf(stderr, "setrlimit returned result = %d\n", ret); + FATAL_ERROR( "Cannot set stack size" ); + } + } + } +} + + +int main( int argc, char *argv[] ) +{ + gid_t gid; + + int ret; + + 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(); */ + + exit_status = get_pkginfo(); + if( exit_status != 0 ) + { + FATAL_ERROR( "%s: Invalid input PKGLOG file", basename( pkglog_fname ) ); + } + + tmpdir = _mk_tmpdir(); + if( !tmpdir ) + { + FATAL_ERROR( "Cannot create temporary directory" ); + } + else + { + char *buf = NULL; + + buf = (char *)malloc( (size_t)strlen( tmpdir ) + 11 ); + if( !buf ) { FATAL_ERROR( "Cannot allocate memory" ); } + + (void)sprintf( (char *)&buf[0], "%s/.REQUIRES", tmpdir ); + requires_fname = xstrdup( (const char *)&buf[0] ); + free( buf ); + } + + /******************* + Getting REQUIRES: + */ + if( (ret = get_requires( (const char *)requires_fname, (const char *)pkglog_fname )) > 0 ) + { + /* We have non-empty list of REQUIRES in the 'requires_fname' file */ + requires = read_requires( (const char *)requires_fname, ret ); + _search_required_packages( (const char *)destination, NULL ); + } + + if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); } + free_resources(); + + exit( exit_status ); +} |