5 kx
5 kx /**********************************************************************
5 kx
5 kx Copyright 2019 Andrey V.Kosteltsev
5 kx
9 kx Licensed under the Radix cross Linux License, Version 1.0 .
5 kx you may not use this file except in compliance with the License.
5 kx You may obtain a copy of the License at
5 kx
9 kx https://radix-linux.su/licenses/LICENSE-1.0-en_US.txt
5 kx
5 kx Unless required by applicable law or agreed to in writing, software
5 kx distributed under the License is distributed on an "AS IS" BASIS,
5 kx WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
5 kx implied.
5 kx
5 kx **********************************************************************/
5 kx
5 kx #include <config.h>
5 kx
5 kx #include <stdlib.h>
5 kx #include <stdio.h>
5 kx #include <sys/types.h>
5 kx #include <stdint.h>
5 kx #include <dirent.h>
5 kx #include <sys/stat.h> /* chmod(2) */
5 kx #include <fcntl.h>
5 kx #include <linux/limits.h>
5 kx #include <alloca.h> /* alloca(3) */
5 kx #include <string.h> /* strdup(3) */
5 kx #include <libgen.h> /* basename(3) */
5 kx #include <ctype.h> /* tolower(3) */
5 kx #include <errno.h>
5 kx #include <time.h>
5 kx #include <sys/time.h>
5 kx #include <pwd.h>
5 kx #include <grp.h>
5 kx #include <stdarg.h>
5 kx #include <unistd.h>
5 kx
5 kx #include <sys/resource.h>
5 kx
5 kx #include <signal.h>
5 kx #if !defined SIGCHLD && defined SIGCLD
5 kx # define SIGCHLD SIGCLD
5 kx #endif
5 kx
5 kx #define _GNU_SOURCE
5 kx #include <getopt.h>
5 kx
5 kx #include <msglog.h>
5 kx #include <wrapper.h>
5 kx #include <system.h>
5 kx
5 kx #define PROGRAM_NAME "pkglog"
5 kx
5 kx #include <defs.h>
5 kx
5 kx
5 kx char *program = PROGRAM_NAME;
5 kx char *destination = NULL, *srcdir = NULL, *pkginfo_fname = NULL, *output_fname = NULL;
5 kx int exit_status = EXIT_SUCCESS; /* errors counter */
5 kx char *selfdir = NULL;
5 kx
5 kx int mkgroupdir = 0;
5 kx
5 kx int rm_srcdir_at_exit = 0;
5 kx
5 kx char *pkgname = NULL,
5 kx *pkgver = NULL,
5 kx *arch = NULL,
5 kx *distroname = NULL,
5 kx *distrover = NULL,
5 kx *group = NULL,
5 kx *url = NULL,
5 kx *license = NULL,
5 kx *uncompressed_size = NULL,
5 kx *total_files = NULL;
5 kx
5 kx FILE *pkginfo = NULL;
5 kx FILE *output = NULL;
5 kx
5 kx
5 kx #define FREE_PKGLOG_VARIABLES() \
5 kx if( pkgname ) { free( pkgname ); } pkgname = NULL; \
5 kx if( pkgver ) { free( pkgver ); } pkgver = NULL; \
5 kx if( arch ) { free( arch ); } arch = NULL; \
5 kx if( distroname ) { free( distroname ); } distroname = NULL; \
5 kx if( distrover ) { free( distrover ); } distrover = NULL; \
5 kx if( group ) { free( group ); } group = NULL; \
5 kx if( url ) { free( url ); } url = NULL; \
5 kx if( license ) { free( license ); } license = NULL; \
5 kx if( uncompressed_size ) { free( uncompressed_size ); } uncompressed_size = NULL; \
5 kx if( total_files ) { free( total_files ); } total_files = NULL
5 kx
5 kx void free_resources()
5 kx {
5 kx if( selfdir ) { free( selfdir ); selfdir = NULL; }
5 kx if( srcdir ) { free( srcdir ); srcdir = NULL; }
5 kx if( destination ) { free( destination ); destination = NULL; }
5 kx if( pkginfo_fname ) { free( pkginfo_fname ); pkginfo_fname = NULL; }
5 kx if( output_fname )
5 kx {
5 kx if( output ) { (void)fflush( output ); fclose( output ); output = NULL; }
5 kx free( output_fname ); output_fname = NULL;
5 kx }
5 kx
5 kx FREE_PKGLOG_VARIABLES();
5 kx }
5 kx
5 kx void usage()
5 kx {
5 kx free_resources();
5 kx
5 kx fprintf( stdout, "\n" );
5 kx fprintf( stdout, "Usage: %s [options] <dir|pkginfo|package>\n", program );
5 kx fprintf( stdout, "\n" );
5 kx fprintf( stdout, "Read information from package's service files and create PKGLOG file\n" );
5 kx fprintf( stdout, "in the destination directory. <pkginfo> is a full name of '.PKGINFO'\n" );
5 kx fprintf( stdout, "service file of package. Rest of package's serfice files should be\n" );
5 kx fprintf( stdout, "present in the directory of '.PKGINFO'. If last argument is <dir>\n" );
5 kx fprintf( stdout, "then pkglog utility will try to read '.PKGINFO' and rest of service\n" );
5 kx fprintf( stdout, "files from directory given by <dir> argument.\n" );
5 kx fprintf( stdout, "\n" );
5 kx fprintf( stdout, "Options:\n" );
5 kx fprintf( stdout, " -h,--help Display this information.\n" );
5 kx fprintf( stdout, " -v,--version Display the version of %s utility.\n", program );
5 kx fprintf( stdout, " -d,--destination=<DIR> Target directory to save output PKGLOG.\n" );
5 kx fprintf( stdout, " -m,--mkgroupdir Create group subdirectory in the PKGLOG\n" );
5 kx fprintf( stdout, " target directory.\n" );
5 kx fprintf( stdout, "\n" );
5 kx fprintf( stdout, "Parameter:\n" );
5 kx fprintf( stdout, " <dir|pkginfo|package> Directory wich contains the package's\n" );
5 kx fprintf( stdout, " service files or path to .PKGINFO file\n" );
5 kx fprintf( stdout, " or package tarball.\n" );
5 kx fprintf( stdout, "\n" );
5 kx
5 kx exit( EXIT_FAILURE );
5 kx }
5 kx
5 kx void to_lowercase( char *s )
5 kx {
5 kx char *p = s;
5 kx while( p && *p ) { int c = *p; *p = tolower( c ); ++p; }
5 kx }
5 kx
5 kx void to_uppercase( char *s )
5 kx {
5 kx char *p = s;
5 kx while( p && *p ) { int c = *p; *p = toupper( c ); ++p; }
5 kx }
5 kx
5 kx void version()
5 kx {
5 kx char *upper = NULL;
5 kx
5 kx upper = (char *)alloca( strlen( program ) + 1 );
5 kx
5 kx strcpy( (char *)upper, (const char *)program );
5 kx to_uppercase( upper );
5 kx
5 kx fprintf( stdout, "%s (%s) %s\n", program, upper, PROGRAM_VERSION );
5 kx
5 kx fprintf( stdout, "Copyright (C) 2019 Andrey V.Kosteltsev.\n" );
5 kx fprintf( stdout, "This is free software. There is NO warranty; not even\n" );
5 kx fprintf( stdout, "for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n" );
5 kx fprintf( stdout, "\n" );
5 kx
5 kx free_resources();
5 kx exit( EXIT_SUCCESS );
5 kx }
5 kx
5 kx
5 kx static void remove_trailing_slash( char *dir )
5 kx {
5 kx char *s;
5 kx
5 kx if( !dir || dir[0] == '\0' ) return;
5 kx
5 kx s = dir + strlen( dir ) - 1;
5 kx while( *s == '/' )
5 kx {
5 kx *s = '\0'; --s;
5 kx }
5 kx }
5 kx
5 kx
5 kx static int _mkdir_p( const char *dir, const mode_t mode )
5 kx {
5 kx char *buf;
5 kx char *p = NULL;
5 kx struct stat sb;
5 kx
5 kx if( !dir ) return -1;
5 kx
5 kx buf = (char *)alloca( strlen( dir ) + 1 );
5 kx strcpy( buf, dir );
5 kx
5 kx remove_trailing_slash( buf );
5 kx
5 kx /* check if path exists and is a directory */
5 kx if( stat( buf, &sb ) == 0 )
5 kx {
5 kx if( S_ISDIR(sb.st_mode) )
5 kx {
5 kx return 0;
5 kx }
5 kx }
5 kx
5 kx /* mkdir -p */
5 kx for( p = buf + 1; *p; ++p )
5 kx {
5 kx if( *p == '/' )
5 kx {
5 kx *p = 0;
5 kx /* test path */
5 kx if( stat( buf, &sb ) != 0 )
5 kx {
5 kx /* path does not exist - create directory */
5 kx if( mkdir( buf, mode ) < 0 )
5 kx {
5 kx return -1;
5 kx }
5 kx } else if( !S_ISDIR(sb.st_mode) )
5 kx {
5 kx /* not a directory */
5 kx return -1;
5 kx }
5 kx *p = '/';
5 kx }
5 kx }
5 kx
5 kx /* test path */
5 kx if( stat( buf, &sb ) != 0 )
5 kx {
5 kx /* path does not exist - create directory */
5 kx if( mkdir( buf, mode ) < 0 )
5 kx {
5 kx return -1;
5 kx }
5 kx } else if( !S_ISDIR(sb.st_mode) )
5 kx {
5 kx /* not a directory */
5 kx return -1;
5 kx }
5 kx
5 kx return 0;
5 kx }
5 kx
5 kx
5 kx static void _rm_tmpdir( const char *dirpath )
5 kx {
5 kx DIR *dir;
5 kx char *path;
5 kx size_t len;
5 kx
5 kx struct stat path_sb, entry_sb;
5 kx struct dirent *entry;
5 kx
5 kx if( stat( dirpath, &path_sb ) == -1 )
5 kx {
5 kx return; /* stat returns error code; errno is set */
5 kx }
5 kx
5 kx if( S_ISDIR(path_sb.st_mode) == 0 )
5 kx {
5 kx return; /* dirpath is not a directory */
5 kx }
5 kx
5 kx if( (dir = opendir(dirpath) ) == NULL )
5 kx {
5 kx return; /* Cannot open direcroty; errno is set */
5 kx }
5 kx
5 kx len = strlen( dirpath );
5 kx
5 kx while( (entry = readdir( dir )) != NULL)
5 kx {
5 kx
5 kx /* skip entries '.' and '..' */
5 kx if( ! strcmp( entry->d_name, "." ) || ! strcmp( entry->d_name, ".." ) ) continue;
5 kx
5 kx /* determinate a full name of an entry */
5 kx path = alloca( len + strlen( entry->d_name ) + 2 );
5 kx strcpy( path, dirpath );
5 kx strcat( path, "/" );
5 kx strcat( path, entry->d_name );
5 kx
5 kx if( stat( path, &entry_sb ) == 0 )
5 kx {
5 kx if( S_ISDIR(entry_sb.st_mode) )
5 kx {
5 kx /* recursively remove a nested directory */
5 kx _rm_tmpdir( path );
5 kx }
5 kx else
5 kx {
5 kx /* remove a file object */
5 kx (void)unlink( path );
5 kx }
5 kx }
5 kx /* else { stat() returns error code; errno is set; and we have to continue the loop } */
5 kx
5 kx }
5 kx
5 kx /* remove the devastated directory and close the object of this directory */
5 kx (void)rmdir( dirpath );
5 kx
5 kx closedir( dir );
5 kx }
5 kx
5 kx
5 kx static char *_mk_tmpdir( void )
5 kx {
5 kx char *buf = NULL, *p, *tmp = "/tmp";
5 kx size_t len = 0, size = 0;
5 kx
5 kx (void)umask( S_IWGRP | S_IWOTH ); /* octal 022 */
5 kx
5 kx /* Get preferred directory for tmp files */
5 kx if( (p = getenv( "TMP" )) != NULL ) {
5 kx tmp = p;
5 kx }
5 kx else if( (p = getenv( "TEMP" )) != NULL ) {
5 kx tmp = p;
5 kx }
5 kx
5 kx size = strlen( tmp ) + strlen( DISTRO_NAME ) + strlen( program ) + 12;
5 kx
5 kx buf = (char *)malloc( size );
5 kx if( !buf ) return NULL;
5 kx
5 kx len = snprintf( buf, size, (const char *)"%s/%s/%s-%.7u", tmp, DISTRO_NAME, program, getpid() );
5 kx if( len == 0 || len == size - 1 )
5 kx {
5 kx free( buf ); return NULL;
5 kx }
5 kx
5 kx _rm_tmpdir( (const char *)&buf[0] );
5 kx
5 kx if( _mkdir_p( buf, S_IRWXU | S_IRWXG | S_IRWXO ) == 0 )
5 kx {
5 kx return buf;
5 kx }
5 kx
5 kx free( buf ); return NULL;
5 kx }
5 kx
5 kx
5 kx void fatal_error_actions( void )
5 kx {
5 kx logmsg( errlog, MSG_NOTICE, "Free resources on FATAL error..." );
5 kx if( rm_srcdir_at_exit ) _rm_tmpdir( (const char *)srcdir );
5 kx if( output_fname )
5 kx {
5 kx if( output ) { (void)fflush( output ); fclose( output ); output = NULL; }
5 kx (void)unlink( output_fname );
5 kx free( output_fname ); output_fname = NULL;
5 kx }
5 kx free_resources();
5 kx }
5 kx
5 kx void sigint( int signum )
5 kx {
5 kx (void)signum;
5 kx
5 kx if( rm_srcdir_at_exit ) _rm_tmpdir( (const char *)srcdir );
5 kx if( output_fname )
5 kx {
5 kx if( output ) { (void)fflush( output ); fclose( output ); output = NULL; }
5 kx (void)unlink( output_fname );
5 kx free( output_fname ); output_fname = NULL;
5 kx }
5 kx free_resources();
5 kx }
5 kx
5 kx static void set_signal_handlers()
5 kx {
5 kx struct sigaction sa;
5 kx sigset_t set;
5 kx
5 kx memset( &sa, 0, sizeof( sa ) );
5 kx sa.sa_handler = sigint; /* TERM, INT */
5 kx sa.sa_flags = SA_RESTART;
5 kx sigemptyset( &set );
5 kx sigaddset( &set, SIGTERM );
5 kx sigaddset( &set, SIGINT );
5 kx sa.sa_mask = set;
5 kx sigaction( SIGTERM, &sa, NULL );
5 kx sigaction( SIGINT, &sa, NULL );
5 kx
5 kx memset( &sa, 0, sizeof( sa ) ); /* ignore SIGPIPE */
5 kx sa.sa_handler = SIG_IGN;
5 kx sa.sa_flags = 0;
5 kx sigaction( SIGPIPE, &sa, NULL );
5 kx
5 kx /* System V fork+wait does not work if SIGCHLD is ignored */
5 kx signal( SIGCHLD, SIG_DFL );
5 kx }
5 kx
5 kx enum _pkginfo_type
5 kx {
5 kx PKGINFO_TEXT = 0,
5 kx PKGINFO_GZ,
5 kx PKGINFO_BZ2,
5 kx PKGINFO_XZ,
5 kx PKGINFO_TAR,
5 kx
5 kx PKGINFO_UNKNOWN
5 kx };
5 kx
5 kx static enum _pkginfo_type pkginfo_type = PKGINFO_UNKNOWN;
5 kx static char uncompress[2] = { 0, 0 };
5 kx
5 kx
5 kx static enum _pkginfo_type check_pkginfo_file( const char *fname )
5 kx {
5 kx struct stat st;
5 kx size_t pkginfo_size = 0;
5 kx unsigned char buf[8];
5 kx int rc, fd;
5 kx
5 kx /* SIGNATURES: https://www.garykessler.net/library/file_sigs.html */
5 kx
5 kx uncompress[0] = '\0';
5 kx
5 kx if( stat( fname, &st ) == -1 )
5 kx {
5 kx FATAL_ERROR( "Cannot access %s file: %s", basename( (char *)fname ), strerror( errno ) );
5 kx }
5 kx
5 kx pkginfo_size = st.st_size;
5 kx
5 kx if( (fd = open( fname, O_RDONLY )) == -1 )
5 kx {
5 kx FATAL_ERROR( "Cannot open %s file: %s", basename( (char *)fname ), strerror( errno ) );
5 kx }
5 kx
5 kx rc = (int)read( fd, (void *)&buf[0], 7 );
5 kx if( rc != 7 )
5 kx {
5 kx FATAL_ERROR( "Unknown type of input file %s", basename( (char *)fname ) );
5 kx }
5 kx buf[7] = '\0';
5 kx
5 kx /* TEXT */
5 kx if( !strncmp( (const char *)&buf[0], "pkgname", 7 ) )
5 kx {
5 kx close( fd ); return PKGINFO_TEXT;
5 kx }
5 kx
5 kx /* GZ */
5 kx if( buf[0] == 0x1F && buf[1] == 0x8B && buf[2] == 0x08 )
5 kx {
5 kx uncompress[0] = 'x';
5 kx close( fd ); return PKGINFO_GZ;
5 kx }
5 kx
5 kx /* BZ2 */
5 kx if( buf[0] == 0x42 && buf[1] == 0x5A && buf[2] == 0x68 )
5 kx {
5 kx uncompress[0] = 'j';
5 kx close( fd ); return PKGINFO_BZ2;
5 kx }
5 kx
5 kx /* XZ */
5 kx if( buf[0] == 0xFD && buf[1] == 0x37 && buf[2] == 0x7A &&
5 kx buf[3] == 0x58 && buf[4] == 0x5A && buf[5] == 0x00 )
5 kx {
5 kx uncompress[0] = 'J';
5 kx close( fd ); return PKGINFO_XZ;
5 kx }
5 kx
5 kx if( pkginfo_size > 262 )
5 kx {
5 kx if( lseek( fd, 257, SEEK_SET ) == -1 )
5 kx {
5 kx FATAL_ERROR( "Cannot check signature of %s file: %s", basename( (char *)fname ), strerror( errno ) );
5 kx }
5 kx rc = (int)read( fd, &buf[0], 5 );
5 kx if( rc != 5 )
5 kx {
5 kx FATAL_ERROR( "Cannot read signature of %s file", basename( (char *)fname ) );
5 kx }
5 kx /* TAR */
5 kx if( buf[0] == 0x75 && buf[1] == 0x73 && buf[2] == 0x74 && buf[3] == 0x61 && buf[4] == 0x72 )
5 kx {
5 kx close( fd ); return PKGINFO_TAR;
5 kx }
5 kx }
5 kx
5 kx close( fd ); return PKGINFO_UNKNOWN;
5 kx }
5 kx
5 kx
5 kx void get_args( int argc, char *argv[] )
5 kx {
5 kx const char* short_options = "hvmd:";
5 kx
5 kx const struct option long_options[] =
5 kx {
5 kx { "help", no_argument, NULL, 'h' },
5 kx { "version", no_argument, NULL, 'v' },
5 kx { "destination", required_argument, NULL, 'd' },
5 kx { "mkgroupdir", no_argument, NULL, 'm' },
5 kx { NULL, 0, NULL, 0 }
5 kx };
5 kx
5 kx int ret;
5 kx int option_index = 0;
5 kx
5 kx while( (ret = getopt_long( argc, argv, short_options, long_options, &option_index )) != -1 )
5 kx {
5 kx switch( ret )
5 kx {
5 kx case 'h':
5 kx {
5 kx usage();
5 kx break;
5 kx }
5 kx case 'v':
5 kx {
5 kx version();
5 kx break;
5 kx }
5 kx
5 kx case 'd':
5 kx {
5 kx if( optarg != NULL )
5 kx {
5 kx destination = xstrdup( (const char *)optarg );
5 kx remove_trailing_slash( destination );
5 kx }
5 kx else
5 kx /* option is present but without value */
5 kx usage();
5 kx break;
5 kx }
5 kx case 'm':
5 kx {
5 kx mkgroupdir = 1;
5 kx break;
5 kx }
5 kx
5 kx case '?': default:
5 kx {
5 kx usage();
5 kx break;
5 kx }
5 kx }
5 kx }
5 kx
5 kx if( destination == NULL )
5 kx {
5 kx char cwd[PATH_MAX];
5 kx if( getcwd( cwd, sizeof(cwd) ) != NULL )
5 kx destination = xstrdup( (const char *)cwd );
5 kx else
5 kx destination = xstrdup( "." );
5 kx }
5 kx
5 kx /* last command line argument is the PKGLOG file */
5 kx if( optind < argc )
5 kx {
5 kx struct stat st;
5 kx char *buf = NULL;
5 kx
5 kx buf = (char *)malloc( strlen( (const char *)argv[optind] ) + 10 );
5 kx if( !buf )
5 kx {
5 kx FATAL_ERROR( "Cannot allocate memory" );
5 kx }
5 kx
5 kx (void)strcpy( buf, (const char *)argv[optind++] );
5 kx remove_trailing_slash( (char *)&buf[0] );
5 kx
5 kx if( stat( (const char *)&buf[0], &st ) == -1 )
5 kx {
5 kx FATAL_ERROR( "Cannot access '%s' file: %s", basename( buf ), strerror( errno ) );
5 kx }
5 kx
5 kx if( S_ISDIR(st.st_mode) )
5 kx {
5 kx /* Add .PKGINFO to the input dir name: */
5 kx (void)strcat( buf, "/.PKGINFO" );
5 kx }
5 kx
5 kx pkginfo_fname = xstrdup( (const char *)&buf[0] );
5 kx if( pkginfo_fname == NULL )
5 kx {
5 kx usage();
5 kx }
5 kx
5 kx free( buf );
5 kx
5 kx pkginfo_type = check_pkginfo_file( (const char *)pkginfo_fname );
5 kx if( pkginfo_type == PKGINFO_UNKNOWN )
5 kx {
5 kx ERROR( "%s: Unknown input file format", basename( pkginfo_fname ) );
5 kx usage();
5 kx }
5 kx }
5 kx else
5 kx {
5 kx usage();
5 kx }
5 kx }
5 kx
5 kx
5 kx /*
5 kx Especialy for pkginfo lines.
5 kx Remove leading spaces and take non-space characters only:
5 kx */
5 kx static char *skip_spaces( char *s )
5 kx {
5 kx char *q, *p = (char *)0;
5 kx
5 kx if( !s || *s == '\0' ) return p;
5 kx
5 kx p = s;
5 kx
5 kx while( (*p == ' ' || *p == '\t') && *p != '\0' ) { ++p; } q = p;
5 kx while( *q != ' ' && *q != '\t' && *q != '\0' ) { ++q; } *q = '\0';
5 kx
5 kx if( *p == '\0' ) return (char *)0;
5 kx
5 kx return( xstrdup( (const char *)p ) );
5 kx }
5 kx
5 kx /*
5 kx remove spaces at end of line:
5 kx */
5 kx static void skip_eol_spaces( char *s )
5 kx {
5 kx char *p = (char *)0;
5 kx
5 kx if( !s || *s == '\0' ) return;
5 kx
5 kx p = s + strlen( s ) - 1;
5 kx while( isspace( *p ) ) { *p-- = '\0'; }
5 kx }
5 kx
5 kx
5 kx void write_pkginfo( void )
5 kx {
5 kx char *ln = NULL;
5 kx char *line = NULL;
5 kx
5 kx if( pkginfo_fname != NULL )
5 kx {
5 kx pkginfo = fopen( (const char *)pkginfo_fname, "r" );
5 kx if( !pkginfo )
5 kx {
5 kx FATAL_ERROR( "Cannot open %s file", pkginfo_fname );
5 kx }
5 kx }
5 kx
5 kx line = (char *)malloc( (size_t)PATH_MAX );
5 kx if( !line )
5 kx {
5 kx FATAL_ERROR( "Cannot allocate memory" );
5 kx }
5 kx
5 kx while( (ln = fgets( line, PATH_MAX, pkginfo )) )
5 kx {
5 kx char *match = NULL;
5 kx
5 kx ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */
5 kx skip_eol_spaces( ln ); /* remove spaces at end-of-line */
5 kx
5 kx if( (match = strstr( ln, "pkgname" )) && match == ln ) {
5 kx char *p = index( match, '=' ) + 1;
5 kx if( p != NULL ) pkgname = skip_spaces( p );
5 kx }
5 kx if( (match = strstr( ln, "pkgver" )) && match == ln ) {
5 kx char *p = index( match, '=' ) + 1;
5 kx if( p != NULL ) pkgver = skip_spaces( p );
5 kx }
5 kx if( (match = strstr( ln, "arch" )) && match == ln ) {
5 kx char *p = index( match, '=' ) + 1;
5 kx if( p != NULL ) arch = skip_spaces( p );
5 kx }
5 kx if( (match = strstr( ln, "distroname" )) && match == ln ) {
5 kx char *p = index( match, '=' ) + 1;
5 kx if( p != NULL ) distroname = skip_spaces( p );
5 kx }
5 kx if( (match = strstr( ln, "distrover" )) && match == ln ) {
5 kx char *p = index( match, '=' ) + 1;
5 kx if( p != NULL ) distrover = skip_spaces( p );
5 kx }
5 kx
5 kx
5 kx if( (match = strstr( ln, "group" )) && match == ln ) {
5 kx char *p = index( match, '=' ) + 1;
5 kx if( p != NULL ) group = skip_spaces( p );
5 kx }
5 kx /* variable short_description="..." is not stored in the PKGLOG file */
5 kx if( (match = strstr( ln, "url" )) && match == ln ) {
5 kx char *p = index( match, '=' ) + 1;
5 kx if( p != NULL ) url = skip_spaces( p );
5 kx }
5 kx if( (match = strstr( ln, "license" )) && match == ln ) {
5 kx char *p = index( match, '=' ) + 1;
5 kx if( p != NULL ) license = skip_spaces( p );
5 kx }
5 kx if( (match = strstr( ln, "uncompressed_size" )) && match == ln ) {
5 kx char *p = index( match, '=' ) + 1;
5 kx if( p != NULL ) uncompressed_size = skip_spaces( p );
5 kx }
5 kx if( (match = strstr( ln, "total_files" )) && match == ln ) {
5 kx char *p = index( match, '=' ) + 1;
5 kx if( p != NULL ) total_files = skip_spaces( p );
5 kx }
5 kx }
5 kx
5 kx free( line );
5 kx
5 kx if( pkgname && pkgver && arch && distroname && distrover )
5 kx {
5 kx int len;
5 kx char *buf = NULL;
5 kx
5 kx buf = (char *)malloc( (size_t)PATH_MAX );
5 kx if( !buf )
5 kx {
5 kx FATAL_ERROR( "Cannot allocate memory" );
5 kx }
5 kx
5 kx if( mkgroupdir && group )
5 kx {
5 kx len = snprintf( buf, PATH_MAX, "%s/%s",
5 kx destination, group );
5 kx if( len == 0 || len == PATH_MAX - 1 )
5 kx {
5 kx FATAL_ERROR( "Cannot create output file" );
5 kx }
5 kx
5 kx if( _mkdir_p( buf, S_IRWXU | S_IRWXG | S_IRWXO ) != 0 )
5 kx {
5 kx FATAL_ERROR( "Cannot create output directory" );
5 kx }
5 kx
5 kx len = snprintf( buf, PATH_MAX, "%s/%s/%s-%s-%s-%s-%s",
5 kx destination, group, pkgname, pkgver, arch, distroname, distrover );
5 kx if( len == 0 || len == PATH_MAX - 1 )
5 kx {
5 kx FATAL_ERROR( "Cannot create output file" );
5 kx }
5 kx }
5 kx else
5 kx {
5 kx len = snprintf( buf, PATH_MAX, "%s/%s-%s-%s-%s-%s",
5 kx destination, pkgname, pkgver, arch, distroname, distrover );
5 kx if( len == 0 || len == PATH_MAX - 1 )
5 kx {
5 kx FATAL_ERROR( "Cannot create output file" );
5 kx }
5 kx }
5 kx output_fname = xstrdup( (const char *)&buf[0] );
5 kx if( output_fname )
5 kx {
5 kx output = fopen( (const char *)output_fname, "w" );
5 kx if( !output )
5 kx {
5 kx FATAL_ERROR( "Cannot create %s file", output_fname );
5 kx }
5 kx }
5 kx
5 kx free( buf );
5 kx
5 kx fprintf( output, "PACKAGE NAME: %s\n", pkgname );
5 kx fprintf( output, "PACKAGE VERSION: %s\n", pkgver );
5 kx fprintf( output, "ARCH: %s\n", arch );
5 kx fprintf( output, "DISTRO: %s\n", distroname );
5 kx fprintf( output, "DISTRO VERSION: %s\n", distrover );
5 kx }
5 kx else
5 kx {
5 kx FATAL_ERROR( "Invalid input .PKGINFO file" );
5 kx }
5 kx
5 kx if( group )
5 kx fprintf( output, "GROUP: %s\n", group );
5 kx if( url )
5 kx fprintf( output, "URL: %s\n", url );
5 kx if( license )
5 kx fprintf( output, "LICENSE: %s\n", license );
5 kx if( uncompressed_size )
5 kx fprintf( output, "UNCOMPRESSED SIZE: %s\n", uncompressed_size );
5 kx if( total_files )
5 kx fprintf( output, "TOTAL FILES: %s\n", total_files );
5 kx
5 kx /* reference counter of not installed package is always zero */
5 kx fprintf( output, "REFERENCE COUNTER: 0\n" );
5 kx }
5 kx
5 kx
5 kx void write_requires( void )
5 kx {
5 kx struct stat sb;
5 kx char *buf = NULL;
5 kx
5 kx if( output == NULL && output_fname == NULL )
5 kx {
5 kx FATAL_ERROR( "Unable to access output file" );
5 kx }
5 kx
5 kx buf = (char *)malloc( (size_t)PATH_MAX );
5 kx if( !buf )
5 kx {
5 kx FATAL_ERROR( "Cannot allocate memory" );
5 kx }
5 kx
5 kx fprintf( output, "REQUIRES:\n" );
5 kx
5 kx bzero( (void *)buf, PATH_MAX );
5 kx (void)sprintf( (char *)&buf[0], "%s/.REQUIRES", srcdir );
5 kx
5 kx /* check if path exists and is a regular file */
5 kx if( stat( (const char *)&buf[0], &sb ) == 0 && S_ISREG(sb.st_mode) )
5 kx {
5 kx char *ln = NULL;
5 kx char *line = NULL;
5 kx
5 kx FILE *input;
5 kx
5 kx input = fopen( (const char *)&buf[0], "r" );
5 kx if( !input )
5 kx {
5 kx FATAL_ERROR( "Unable to access %s file", (char *)&buf[0] );
5 kx }
5 kx
5 kx line = (char *)malloc( (size_t)PATH_MAX );
5 kx if( !line )
5 kx {
5 kx FATAL_ERROR( "Cannot allocate memory" );
5 kx }
5 kx
5 kx /* cat .REQUIRES >> PKGLOG */
5 kx while( (ln = fgets( line, PATH_MAX, input )) )
5 kx {
5 kx ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */
5 kx skip_eol_spaces( ln ); /* remove spaces at end-of-line */
5 kx
5 kx /* print non-empty lines */
5 kx if( *ln )
5 kx {
5 kx fprintf( output, "%s\n", ln );
5 kx }
5 kx }
5 kx free( line );
5 kx }
5 kx free( buf );
5 kx }
5 kx
5 kx
5 kx void write_description( void )
5 kx {
5 kx struct stat sb;
5 kx char *buf = NULL;
5 kx
5 kx if( output == NULL && output_fname == NULL )
5 kx {
5 kx FATAL_ERROR( "Unable to access output file" );
5 kx }
5 kx
5 kx buf = (char *)malloc( (size_t)PATH_MAX );
5 kx if( !buf )
5 kx {
5 kx FATAL_ERROR( "Cannot allocate memory" );
5 kx }
5 kx
5 kx fprintf( output, "PACKAGE DESCRIPTION:\n" );
5 kx
5 kx bzero( (void *)buf, PATH_MAX );
5 kx (void)sprintf( (char *)&buf[0], "%s/.DESCRIPTION", srcdir );
5 kx
5 kx /* check if path exists and is a regular file */
5 kx if( stat( (const char *)&buf[0], &sb ) == 0 && S_ISREG(sb.st_mode) )
5 kx {
5 kx char *ln = NULL;
5 kx char *line = NULL;
5 kx char *pattern = NULL;
5 kx int n = 0;
5 kx
5 kx FILE *input;
5 kx
5 kx input = fopen( (const char *)&buf[0], "r" );
5 kx if( !input )
5 kx {
5 kx FATAL_ERROR( "Unable to access %s file", (char *)&buf[0] );
5 kx }
5 kx
5 kx line = (char *)malloc( (size_t)PATH_MAX );
5 kx if( !line ) { FATAL_ERROR( "Cannot allocate memory" ); }
5 kx
5 kx pattern = (char *)malloc( (size_t)strlen( pkgname ) + 2 );
5 kx if( !pattern ) { FATAL_ERROR( "Cannot allocate memory" ); }
5 kx
5 kx (void)sprintf( pattern, "%s:", pkgname );
5 kx
5 kx /* cat .DESCRIPTION >> PKGLOG */
5 kx while( (ln = fgets( line, PATH_MAX, input )) )
5 kx {
5 kx char *match = NULL;
5 kx
5 kx ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */
5 kx skip_eol_spaces( ln ); /* remove spaces at end-of-line */
5 kx
5 kx /*
5 kx skip non-significant spaces at beginning of line
5 kx and print lines started with 'pkgname:'
5 kx */
5 kx if( (match = strstr( ln, pattern )) && n < DESCRIPTION_NUMBER_OF_LINES )
5 kx {
5 kx int mlen = strlen( match ), plen = strlen( pattern );
5 kx int length = ( mlen > plen ) ? (mlen - plen - 1) : 0 ;
5 kx
5 kx if( length > DESCRIPTION_LENGTH_OF_LINE )
5 kx {
5 kx /* WARNING( "Package DESCRIPTION contains lines with length greater than %d characters", DESCRIPTION_LENGTH_OF_LINE ); */
5 kx match[plen + 1 + DESCRIPTION_LENGTH_OF_LINE] = '\0'; /* truncating description line */
5 kx skip_eol_spaces( match ); /* remove spaces at end-of-line */
5 kx }
5 kx fprintf( output, "%s\n", match );
5 kx ++n;
5 kx }
5 kx }
5 kx
5 kx if( n < DESCRIPTION_NUMBER_OF_LINES )
5 kx {
5 kx /* WARNING( "Package DESCRIPTION contains less than %d lines", DESCRIPTION_NUMBER_OF_LINES ); */
5 kx while( n < DESCRIPTION_NUMBER_OF_LINES )
5 kx {
5 kx fprintf( output, "%s\n", pattern );
5 kx ++n;
5 kx }
5 kx }
5 kx
5 kx free( pattern );
5 kx free( line );
5 kx }
5 kx
5 kx free( buf );
5 kx }
5 kx
5 kx
5 kx void write_restore_links( void )
5 kx {
5 kx struct stat sb;
5 kx char *buf = NULL;
5 kx
5 kx if( output == NULL && output_fname == NULL )
5 kx {
5 kx FATAL_ERROR( "Unable to access output file" );
5 kx }
5 kx
5 kx buf = (char *)malloc( (size_t)PATH_MAX );
5 kx if( !buf )
5 kx {
5 kx FATAL_ERROR( "Cannot allocate memory" );
5 kx }
5 kx
5 kx fprintf( output, "RESTORE LINKS:\n" );
5 kx
5 kx bzero( (void *)buf, PATH_MAX );
5 kx (void)sprintf( (char *)&buf[0], "%s/.RESTORELINKS", srcdir );
5 kx
5 kx /* check if path exists and is a regular file */
5 kx if( stat( (const char *)&buf[0], &sb ) == 0 && S_ISREG(sb.st_mode) )
5 kx {
5 kx char *ln = NULL;
5 kx char *line = NULL;
5 kx
5 kx FILE *input;
5 kx
5 kx input = fopen( (const char *)&buf[0], "r" );
5 kx if( !input )
5 kx {
5 kx FATAL_ERROR( "Unable to access %s file", (char *)&buf[0] );
5 kx }
5 kx
5 kx line = (char *)malloc( (size_t)PATH_MAX );
5 kx if( !line )
5 kx {
5 kx FATAL_ERROR( "Cannot allocate memory" );
5 kx }
5 kx
5 kx /* cat .REQUIRES >> PKGLOG */
5 kx while( (ln = fgets( line, PATH_MAX, input )) )
5 kx {
5 kx ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */
5 kx skip_eol_spaces( ln ); /* remove spaces at end-of-line */
5 kx
5 kx /* print non-empty lines */
5 kx if( *ln )
5 kx {
5 kx fprintf( output, "%s\n", ln );
5 kx }
5 kx }
5 kx
5 kx free( line );
5 kx }
5 kx
5 kx free( buf );
5 kx }
5 kx
5 kx
5 kx void write_install_script( void )
5 kx {
5 kx struct stat sb;
5 kx char *buf = NULL;
5 kx
5 kx if( output == NULL && output_fname == NULL )
5 kx {
5 kx FATAL_ERROR( "Unable to access output file" );
5 kx }
5 kx
5 kx buf = (char *)malloc( (size_t)PATH_MAX );
5 kx if( !buf )
5 kx {
5 kx FATAL_ERROR( "Cannot allocate memory" );
5 kx }
5 kx
5 kx fprintf( output, "INSTALL SCRIPT:\n" );
5 kx
5 kx bzero( (void *)buf, PATH_MAX );
5 kx (void)sprintf( (char *)&buf[0], "%s/.INSTALL", srcdir );
5 kx
5 kx /* check if path exists and is a regular file */
5 kx if( stat( (const char *)&buf[0], &sb ) == 0 && S_ISREG(sb.st_mode) )
5 kx {
5 kx char *ln = NULL;
5 kx char *line = NULL;
5 kx
5 kx FILE *input;
5 kx
5 kx input = fopen( (const char *)&buf[0], "r" );
5 kx if( !input )
5 kx {
5 kx FATAL_ERROR( "Unable to access %s file", (char *)&buf[0] );
5 kx }
5 kx
5 kx line = (char *)malloc( (size_t)PATH_MAX );
5 kx if( !line )
5 kx {
5 kx FATAL_ERROR( "Cannot allocate memory" );
5 kx }
5 kx
5 kx /* cat .REQUIRES >> PKGLOG */
5 kx while( (ln = fgets( line, PATH_MAX, input )) )
5 kx {
5 kx ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */
5 kx skip_eol_spaces( ln ); /* remove spaces at end-of-line */
5 kx
5 kx /* print all lines */
5 kx fprintf( output, "%s\n", ln );
5 kx }
5 kx free( line );
5 kx }
5 kx else
5 kx {
5 kx FATAL_ERROR( "Package doesn't contains the INSTALL script" );
5 kx }
5 kx
5 kx free( buf );
5 kx }
5 kx
5 kx
5 kx void write_file_list( void )
5 kx {
5 kx struct stat sb;
5 kx char *buf = NULL;
5 kx
5 kx if( output == NULL && output_fname == NULL )
5 kx {
5 kx FATAL_ERROR( "Unable to access output file" );
5 kx }
5 kx
5 kx buf = (char *)malloc( (size_t)PATH_MAX );
5 kx if( !buf )
5 kx {
5 kx FATAL_ERROR( "Cannot allocate memory" );
5 kx }
5 kx
5 kx fprintf( output, "FILE LIST:\n" );
5 kx
5 kx bzero( (void *)buf, PATH_MAX );
5 kx (void)sprintf( (char *)&buf[0], "%s/.FILELIST", srcdir );
5 kx
5 kx /* check if path exists and is a regular file */
5 kx if( stat( (const char *)&buf[0], &sb ) == 0 && S_ISREG(sb.st_mode) )
5 kx {
5 kx char *ln;
5 kx char *line = NULL;
5 kx
5 kx FILE *input;
5 kx
5 kx input = fopen( (const char *)&buf[0], "r" );
5 kx if( !input )
5 kx {
5 kx FATAL_ERROR( "Unable to access %s file", (char *)&buf[0] );
5 kx }
5 kx
5 kx line = (char *)malloc( (size_t)PATH_MAX );
5 kx if( !line )
5 kx {
5 kx FATAL_ERROR( "Cannot allocate memory" );
5 kx }
5 kx
5 kx /* cat .REQUIRES >> PKGLOG */
5 kx while( (ln = fgets( line, PATH_MAX, input )) )
5 kx {
5 kx ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */
5 kx skip_eol_spaces( ln ); /* remove spaces at end-of-line */
5 kx
5 kx /* print non-empty lines */
5 kx if( *ln )
5 kx {
5 kx fprintf( output, "%s\n", ln );
5 kx }
5 kx }
5 kx
5 kx free( line );
5 kx }
5 kx else
5 kx {
5 kx FATAL_ERROR( "Package doesn't contains the FILE list" );
5 kx }
5 kx
5 kx free( buf );
5 kx }
5 kx
5 kx
5 kx /*********************************************
5 kx Get directory where this program is placed:
5 kx */
5 kx char *get_selfdir( void )
5 kx {
5 kx char *buf = NULL;
5 kx ssize_t len;
5 kx
5 kx buf = (char *)malloc( (size_t)PATH_MAX );
5 kx if( !buf )
5 kx {
5 kx FATAL_ERROR( "Cannot allocate memory" );
5 kx }
5 kx
5 kx bzero( (void *)buf, PATH_MAX );
5 kx len = readlink( "/proc/self/exe", buf, (size_t)PATH_MAX );
5 kx if( len > 0 && len < PATH_MAX )
5 kx {
5 kx char *p = xstrdup( (const char *)dirname( buf ) );
5 kx free( buf );
5 kx return p;
5 kx }
5 kx FATAL_ERROR( "Cannot determine self directory. Please mount /proc filesystem" );
5 kx }
5 kx
5 kx void set_stack_size( void )
5 kx {
5 kx const rlim_t stack_size = 16 * 1024 * 1024; /* min stack size = 16 MB */
5 kx struct rlimit rl;
5 kx int ret;
5 kx
5 kx ret = getrlimit( RLIMIT_STACK, &rl );
5 kx if( ret == 0 )
5 kx {
5 kx if( rl.rlim_cur < stack_size )
5 kx {
5 kx rl.rlim_cur = stack_size;
5 kx ret = setrlimit( RLIMIT_STACK, &rl );
5 kx if( ret != 0 )
5 kx {
5 kx fprintf(stderr, "setrlimit returned result = %d\n", ret);
5 kx FATAL_ERROR( "Cannot set stack size" );
5 kx }
5 kx }
5 kx }
5 kx }
5 kx
5 kx
5 kx int main( int argc, char *argv[] )
5 kx {
5 kx gid_t gid;
5 kx
5 kx set_signal_handlers();
5 kx gid = getgid();
5 kx setgroups( 1, &gid );
5 kx
5 kx fatal_error_hook = fatal_error_actions;
5 kx
5 kx selfdir = get_selfdir();
5 kx
5 kx errlog = stderr;
5 kx
5 kx program = basename( argv[0] );
5 kx get_args( argc, argv );
5 kx
5 kx /* set_stack_size(); */
5 kx
5 kx if( pkginfo_type != PKGINFO_TEXT )
5 kx {
5 kx /* Create tmpdir */
5 kx srcdir = _mk_tmpdir();
5 kx if( !srcdir )
5 kx {
5 kx FATAL_ERROR( "Cannot create temporary dir" );
5 kx }
5 kx rm_srcdir_at_exit = 1;
5 kx
5 kx /* Unpack SERVICE files */
5 kx {
5 kx pid_t p = (pid_t) -1;
5 kx int rc;
5 kx
5 kx int len = 0;
5 kx char *cmd = NULL, *errmsg = NULL, *wmsg = NULL;
5 kx
5 kx cmd = (char *)malloc( (size_t)PATH_MAX );
5 kx if( !cmd ) { FATAL_ERROR( "Cannot allocate memory" ); }
5 kx
5 kx errmsg = (char *)malloc( (size_t)PATH_MAX );
5 kx if( !errmsg ) { FATAL_ERROR( "Cannot allocate memory" ); }
5 kx
5 kx wmsg = (char *)malloc( (size_t)PATH_MAX );
5 kx if( !wmsg ) { FATAL_ERROR( "Cannot allocate memory" ); }
5 kx
5 kx bzero( (void *)cmd, PATH_MAX );
5 kx bzero( (void *)errmsg, PATH_MAX );
5 kx bzero( (void *)wmsg, PATH_MAX );
5 kx
5 kx (void)sprintf( &errmsg[0], "Cannot get SERVICE files from %s file", basename( pkginfo_fname ) );
5 kx
5 kx len = snprintf( &cmd[0], PATH_MAX, "tar -C %s -x%sf %s %s > /dev/null 2>&1",
5 kx srcdir, uncompress, pkginfo_fname,
5 kx ".PKGINFO .REQUIRES .DESCRIPTION .RESTORELINKS .INSTALL .FILELIST" );
5 kx if( len == 0 || len == PATH_MAX - 1 )
5 kx {
5 kx FATAL_ERROR( errmsg );
5 kx }
5 kx p = sys_exec_command( cmd );
5 kx rc = sys_wait_command( p, (char *)&wmsg[0], PATH_MAX );
5 kx if( rc != 0 )
5 kx {
5 kx if( ! DO_NOT_WARN_ABOUT_SERVICE_FILES )
5 kx {
5 kx /*****************************************
5 kx if( rc > 0 ) { return TAR exit status }
5 kx else { return EXIT_FAILURE }
5 kx */
5 kx if( rc > 0 ) exit_status = rc - 1; /* ERROR() will add one */
5 kx ERROR( errmsg );
5 kx if( fatal_error_hook) fatal_error_hook();
5 kx exit( exit_status );
5 kx }
5 kx }
5 kx
5 kx if( cmd ) free( cmd );
5 kx if( errmsg ) free( errmsg );
5 kx if( wmsg ) free( wmsg );
5 kx }
5 kx
5 kx /* Change input pkginfo file name and type */
5 kx if( pkginfo_fname )
5 kx {
5 kx char *buf = NULL;
5 kx
5 kx buf = (char *)malloc( strlen( pkginfo_fname ) + 10 );
5 kx if( !buf )
5 kx {
5 kx FATAL_ERROR( "Cannot allocate memory" );
5 kx }
5 kx
5 kx (void)sprintf( &buf[0], "%s/.PKGINFO", srcdir );
5 kx
5 kx free( pkginfo_fname ); pkginfo_fname = NULL;
5 kx
5 kx pkginfo_fname = xstrdup( (const char *)buf );
5 kx free( buf );
5 kx pkginfo_type = PKGINFO_TEXT;
5 kx }
5 kx
5 kx }
5 kx else /* TEXT: */
5 kx {
5 kx char *buf = NULL;
5 kx
5 kx buf = (char *)malloc( (size_t)strlen( pkginfo_fname ) + 1 );
5 kx if( !buf )
5 kx {
5 kx FATAL_ERROR( "Cannot allocate memory" );
5 kx }
5 kx
5 kx /* function dirname() spoils the source contents: */
5 kx (void)sprintf( buf, "%s", pkginfo_fname );
5 kx
5 kx srcdir = xstrdup( (const char *)dirname( (char *)&buf[0] ) );
5 kx free( buf );
5 kx rm_srcdir_at_exit = 0;
5 kx }
5 kx
5 kx
5 kx write_pkginfo();
5 kx write_requires();
5 kx write_description();
5 kx write_restore_links();
5 kx write_install_script();
5 kx write_file_list();
5 kx
5 kx
5 kx if( rm_srcdir_at_exit ) _rm_tmpdir( (const char *)srcdir );
5 kx free_resources();
5 kx
5 kx exit( exit_status );
5 kx }