diff --git a/usr.sbin/pkg_install/Makefile b/usr.sbin/pkg_install/Makefile index d4a0baf10478..d89e0acbd050 100644 --- a/usr.sbin/pkg_install/Makefile +++ b/usr.sbin/pkg_install/Makefile @@ -2,7 +2,7 @@ .include -SUBDIR= lib add create delete info ${_sign} version +SUBDIR= lib add create delete info ${_sign} updating version .if ${MK_OPENSSL} != "no" _sign= sign diff --git a/usr.sbin/pkg_install/updating/Makefile b/usr.sbin/pkg_install/updating/Makefile new file mode 100644 index 000000000000..980747eb3bb0 --- /dev/null +++ b/usr.sbin/pkg_install/updating/Makefile @@ -0,0 +1,11 @@ +# $FreeBSD$ + +PROG= pkg_updating +SRCS= main.c + +CFLAGS+= -I${.CURDIR}/../lib + +WARNS?= 6 +WFORMAT?= 1 + +.include diff --git a/usr.sbin/pkg_install/updating/main.c b/usr.sbin/pkg_install/updating/main.c new file mode 100644 index 000000000000..84d089eb62ff --- /dev/null +++ b/usr.sbin/pkg_install/updating/main.c @@ -0,0 +1,257 @@ +/*- + * ---------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * wrote this file. As long as you retain this notice you + * can do whatever you want with this stuff. If we meet some day, and you think + * this stuff is worth it, you can buy me a beer in return. Beat Gätzi + * ---------------------------------------------------------------------------- + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include /* For MAXPATHLEN */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pathnames.h" + +typedef struct installedport { + struct installedport *next; /* List of installed ports. */ + char name[LINE_MAX]; /* Name of the installed port. */ +} INSTALLEDPORT; + +int usage(void); + +/* + * Parse /usr/port/UPDATING for corresponding entries. If no argument is + * passed to pkg_updating all entries for all installed ports are displayed. + * If a list of portnames is passed to pkg_updating only entries for the + * given portnames are displayed. Use the -d option to define that only newer + * entries as this date are shown. + */ +int +main(int argc, char *argv[]) +{ + /* Keyword for searching portname in UPDATING. */ + const char *affects = "AFFECTS"; + /* Indicate a date -> end of a entry. Will fail on 2100-01-01... */ + const char *end = "20"; + /* Keyword for searching origin portname of installed port. */ + const char *origin = "@comment ORIGIN:"; + const char *pkgdbpath = LOG_DIR; /* Location of pkgdb */ + const char *updatingfile = UPDATING; /* Location of UPDATING */ + + char *date = NULL; /* Passed -d argument */ + char *dateline = NULL; /* Saved date of an entry */ + /* Tmp lines for parsing file */ + char *tmpline1 = NULL; + char *tmpline2 = NULL; + + char originline[LINE_MAX]; /* Line of +CONTENTS */ + /* Temporary variable to create path to +CONTENTS for installed ports. */ + char tmp_file[MAXPATHLEN]; + char updatingline[LINE_MAX]; /* Line of UPDATING */ + + int ch; /* Char used by getopt */ + int found = 0; /* Found an entry */ + int linelength; /* Length of parsed line */ + int maxcharperline = LINE_MAX; /* Max chars per line */ + int dflag = 0; /* -d option set */ + /* If pflag = 0 UPDATING will be checked for all installed ports. */ + int pflag = 0; + + size_t n; /* Offset to create path */ + + struct dirent *pkgdbdir; /* pkgdb directory */ + struct stat attribute; /* attribute of pkgdb element */ + + /* Needed nodes for linked list with installed ports. */ + INSTALLEDPORT *head = (INSTALLEDPORT *) NULL; + INSTALLEDPORT *curr = (INSTALLEDPORT *) NULL; + + DIR *dir; + FILE *fd; + + while ((ch = getopt(argc, argv, "f:p:d:")) != -1) { + switch (ch) { + case 'd': + dflag = 1; + date = optarg; + break; + case 'f': + updatingfile = optarg; + break; + case '?': + default: + usage(); + } + } + argc -= optind; + argv += optind; + + /* Check if passed date has a correct format. */ + if (dflag == 1) { + linelength = strlen(date); + if (linelength != 8) + exit(EX_DATAERR); + if (strspn(date, "0123456789") != 8) { + fprintf(stderr, "unknown date format: %s\n", date); + exit(EX_DATAERR); + } + } + + /* Save the list of passed portnames. */ + if (argc != 0) { + pflag = 1; + while (*argv) { + if((curr = (INSTALLEDPORT *) + malloc(sizeof(INSTALLEDPORT))) == NULL) + (void)exit(EXIT_FAILURE); + strlcpy (curr->name, *argv, strlen(*argv) + 1); + curr->next = head; + head = curr; + (void)*argv++; + } + } + + /* + * UPDATING will be parsed for all installed ports + * if no portname is passed. + */ + if (pflag == 0) { + /* Open /var/db/pkg and search for all installed ports. */ + if((dir = opendir(pkgdbpath)) != NULL) { + while ((pkgdbdir = readdir(dir)) != NULL) { + if (strcmp(pkgdbdir->d_name, ".") != 0 && + strcmp(pkgdbdir->d_name, "..") !=0) { + + /* Create path to +CONTENTS file for each installed port */ + n = strlcpy(tmp_file, pkgdbpath, strlen(pkgdbpath)+1); + n = strlcpy(tmp_file + n, "/", sizeof(tmp_file) - n); + n = strlcat(tmp_file + n, pkgdbdir->d_name, + sizeof(tmp_file) - n); + if(stat(tmp_file, &attribute) == -1) { + fprintf(stderr, "can't open %s: %s\n", + tmp_file, strerror(errno)); + return EXIT_FAILURE; + } + if(attribute.st_mode & S_IFREG) + continue; + (void)strlcat(tmp_file + n, "/+CONTENTS", + sizeof(tmp_file) - n); + + /* Open +CONTENT file */ + fd = fopen(tmp_file, "r"); + if(fd == NULL) { + fprintf(stderr, "warning: can't open %s: %s\n", + tmp_file, strerror(errno)); + continue; + } + + /* + * Parses +CONTENT for ORIGIN line and + * put element into linked list. + */ + while(fgets(originline, maxcharperline, fd) != NULL) { + tmpline1 = strstr(originline, origin); + if( tmpline1 != NULL ) { + /* Tmp variable to store port name. */ + char *pname; + pname = strrchr(originline, (int)':'); + pname++; + if((curr = (INSTALLEDPORT *) + malloc(sizeof(INSTALLEDPORT))) == NULL) + (void)exit(EXIT_FAILURE); + strlcpy (curr->name, pname, strlen(pname)+1); + curr->next = head; + head = curr; + } + } + + if(ferror(fd)) { + fprintf(stderr, "error reading input\n"); + exit(EX_IOERR); + } + + (void)fclose(fd); + } + } + closedir(dir); + } + } + + /* Open UPDATING file */ + fd = fopen(updatingfile, "r"); + if(fd == NULL) { + fprintf(stderr, "can't open %s: %s\n", + updatingfile, strerror(errno)); + exit(EX_UNAVAILABLE); + } + + /* Parse opened UPDATING file. */ + while(fgets(updatingline, maxcharperline, fd) != NULL) { + /* No entry is found so far */ + if (found == 0) { + /* Search for AFFECTS line to parse the portname. */ + tmpline1 = strstr(updatingline, affects); + + if( tmpline1 != NULL ) { + curr = head; + while(curr != NULL) { + tmpline2 = strstr(updatingline, curr->name); + if( tmpline2 != NULL ) + break; + curr = curr->next; + } + if( tmpline2 != NULL ) { + /* If -d is set, check if entry is newer than the date. */ + if ( (dflag == 1) && (strncmp(dateline, date, 8) < 0)) + continue; + printf("%s", dateline); + printf("%s", updatingline); + found = 1; + } + } + } + /* Search for the end of an entry, if not found print the line. */ + else { + tmpline1 = strstr(updatingline, end); + if( tmpline1 == NULL ) + printf("%s", updatingline); + else { + linelength = strlen(updatingline); + if (linelength == 10) + found = 0; + else + printf("%s", updatingline); + } + } + /* Save the actual line, it could be a date. */ + dateline = strdup(updatingline); + } + + if(ferror(fd)) { + fprintf(stderr, "error reading input\n"); + exit(EX_IOERR); + } + (void)fclose(fd); + + exit(EX_OK); +} + +int +usage(void) +{ + fprintf(stderr, + "usage: pkg_updating [-d YYYYMMDD] [-f file] [portname ...]\n"); + exit(EX_USAGE); +} diff --git a/usr.sbin/pkg_install/updating/pathnames.h b/usr.sbin/pkg_install/updating/pathnames.h new file mode 100644 index 000000000000..d5ec1a077c45 --- /dev/null +++ b/usr.sbin/pkg_install/updating/pathnames.h @@ -0,0 +1,28 @@ +/*- + * ---------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * wrote this file. As long as you retain this notice you + * can do whatever you want with this stuff. If we meet some day, and you think + * this stuff is worth it, you can buy me a beer in return. Beat Gätzi + * ---------------------------------------------------------------------------- + * + * $FreeBSD$ + * + */ + +/* Copy from ../version/version.h */ + +/* Where the ports lives by default */ +#define DEF_PORTS_DIR "/usr/ports/UPDATING" +/* just in case we change the environment variable name */ +#define PORTSDIR "PORTSDIR" +/* macro to get name of directory where we put logging information */ +#define UPDATING (getenv(PORTSDIR) ? strcat(getenv(PORTSDIR), \ + "/UPDATING") : DEF_PORTS_DIR) + +/* Where we put logging information by default, else ${PKG_DBDIR} if set */ +#define DEF_LOG_DIR "/var/db/pkg" +/* just in case we change the environment variable name */ +#define PKG_DBDIR "PKG_DBDIR" +/* macro to get name of directory where we put logging information */ +#define LOG_DIR (getenv(PKG_DBDIR) ? getenv(PKG_DBDIR) : DEF_LOG_DIR) diff --git a/usr.sbin/pkg_install/updating/pkg_updating.1 b/usr.sbin/pkg_install/updating/pkg_updating.1 new file mode 100644 index 000000000000..16554b613991 --- /dev/null +++ b/usr.sbin/pkg_install/updating/pkg_updating.1 @@ -0,0 +1,88 @@ +.\" +.\" FreeBSD updating - Scan the installed ports and show all UPDATING entries +.\" that affect one of the installed ports. Alternative a list of portnames +.\" could be passed to pkg_updating +.\" +.\" "THE BEER-WARE LICENSE" (Revision 42): +.\" wrote this file. As long as you retain this notice you +.\" can do whatever you want with this stuff. If we meet some day, and you think +.\" this stuff is worth it, you can buy me a beer in return. Beat Gätzi +.\" +.\" $FreeBSD$ +.\" +.Dd November 2, 2007 +.Dt PKG_UPDATING 1 +.Os +.Sh NAME +.Nm pkg_updating +.Nd a utility for displaying UPDATING entries of software packages +.Sh SYNOPSIS +.Nm +.Op Fl d Ar date +.Op Fl f Ar file +.Op Ar pkg-name ... +.Nm +.Sh DESCRIPTION +The +.Nm +command scans the installed ports and show all UPDATING entries that affect one +of the installed ports. Alternative a list of pkg-names could be passed. +.Sh OPTIONS +The following command line options are supported: +.Bl -tag -width indent +.It Ar pkg-name ... +UPDATING entries for the named packages are displayed. +.It Fl d Ar date +Only entries newer than this date are shown. Use a YYYYMMDD date format. +.It Fl f Ar UPDATING file +Defines a alternative location of the UPDATING file. +.El +.Sh EXAMPLES +.Bl -tag -width indent +.Dl pkg_updating +.Pp +Shows all entries of all installed ports. +.Pp +.Dl pkg_updating -d 20070101 +.Pp +Shows all entries of all installed ports since 2007-01-01. +.Pp +.Dl pkg_updating apache mysql +.Pp +Shows all entries for all apache and mysql ports. +.Pp +.Dl pkg_updating -d 20060101 apache +.Pp +Shows all apache entries since 2006-01-01. +.Pp +.Dl pkg_updating -f /tmp/UPDATING +.Pp +Defines that the UPDATING file is in /tmp and shows all entries of all +installed ports +.Pp +.El +.Sh ENVIRONMENT +.Bl -tag -width PKG_DBDIR +.It Ev PKG_DBDIR +Specifies an alternative location for the installed package database. +.It Ev PORTSDIR +Location of the ports tree. +.El +.Sh FILES +.Bl -tag -width /var/db/pkg -compact +.It Pa /var/db/pkg +Default location of the installed package database. +.It Pa /usr/ports +The default ports directory and default location of the UPDATING file +.El +.Sh SEE ALSO +.Xr pkg_add 1 , +.Xr pkg_create 1 , +.Xr pkg_delete 1 , +.Xr pkg_version 1 +.Sh AUTHORS +.An Beat Gätzi Aq beat@chruetertee.ch +.Sh CONTRIBUTORS +.An Martin Tournoij Aq carpetsmoker@xs4all.nl +.Sh BUGS +Sure to be some.