/********************************************************************** 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 #include #include #include #include #include #include /* chmod(2) */ #include /* flock(2) */ #include #include #include /* alloca(3) */ #include /* strdup(3) */ #include /* index(3) */ #include /* basename(3) */ #include /* tolower(3) */ #include #include #include #include #include #include #include #include #include #if !defined SIGCHLD && defined SIGCLD # define SIGCHLD SIGCLD #endif #define _GNU_SOURCE #include #include #include #include #define PROGRAM_NAME "chrefs" #include 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] \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 \n" ); fprintf( stdout, "file is located. If destination is defined then 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= Setup Database packages directory.\n" ); fprintf( stdout, " -o,--operation= Operation: Increment or Decrement.\n" ); fprintf( stdout, "\n" ); fprintf( stdout, "Parameter:\n" ); fprintf( stdout, " 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 ); }