main.c revision 207113
1/*- 2 * ---------------------------------------------------------------------------- 3 * "THE BEER-WARE LICENSE" (Revision 42): 4 * <beat@chruetertee.ch> wrote this file. As long as you retain this notice you 5 * can do whatever you want with this stuff. If we meet some day, and you think 6 * this stuff is worth it, you can buy me a beer in return. Beat G�tzi 7 * ---------------------------------------------------------------------------- 8 */ 9 10#include <sys/cdefs.h> 11__FBSDID("$FreeBSD: head/usr.sbin/pkg_install/updating/main.c 207113 2010-04-23 11:07:43Z flz $"); 12 13 14#include <sys/param.h> 15#include <stdio.h> 16#include <errno.h> 17#include <fetch.h> 18#include <limits.h> 19#include <sysexits.h> 20#include <getopt.h> 21 22#include <pkg.h> 23#include "pathnames.h" 24 25typedef struct installedport { 26 struct installedport *next; /* List of installed ports. */ 27 char name[LINE_MAX]; /* Name of the installed port. */ 28} INSTALLEDPORT; 29 30int usage(void); 31 32static char opts[] = "d:f:h"; 33static struct option longopts[] = { 34 { "date", required_argument, NULL, 'd' }, 35 { "file", required_argument, NULL, 'f' }, 36 { "help", no_argument, NULL, 'h' }, 37 { NULL, 0, NULL, 0 }, 38}; 39 40/* 41 * Parse /usr/port/UPDATING for corresponding entries. If no argument is 42 * passed to pkg_updating all entries for all installed ports are displayed. 43 * If a list of portnames is passed to pkg_updating only entries for the 44 * given portnames are displayed. Use the -d option to define that only newer 45 * entries as this date are shown. 46 */ 47int 48main(int argc, char *argv[]) 49{ 50 /* Keyword for searching portname in UPDATING. */ 51 const char *affects = "AFFECTS"; 52 /* Indicate a date -> end of a entry. Will fail on 2100-01-01... */ 53 const char *end = "20"; 54 /* Keyword for searching origin portname of installed port. */ 55 const char *origin = "@comment ORIGIN:"; 56 const char *pkgdbpath = LOG_DIR; /* Location of pkgdb */ 57 const char *updatingfile = UPDATING; /* Location of UPDATING */ 58 59 char *date = NULL; /* Passed -d argument */ 60 char *dateline = NULL; /* Saved date of an entry */ 61 /* Tmp lines for parsing file */ 62 char *tmpline1 = NULL; 63 char *tmpline2 = NULL; 64 65 char originline[LINE_MAX]; /* Line of +CONTENTS */ 66 /* Temporary variable to create path to +CONTENTS for installed ports. */ 67 char tmp_file[MAXPATHLEN]; 68 char updatingline[LINE_MAX]; /* Line of UPDATING */ 69 70 int ch; /* Char used by getopt */ 71 int found = 0; /* Found an entry */ 72 int linelength; /* Length of parsed line */ 73 int maxcharperline = LINE_MAX; /* Max chars per line */ 74 int dflag = 0; /* -d option set */ 75 /* If pflag = 0 UPDATING will be checked for all installed ports. */ 76 int pflag = 0; 77 78 size_t n; /* Offset to create path */ 79 80 struct dirent *pkgdbdir; /* pkgdb directory */ 81 struct stat attribute; /* attribute of pkgdb element */ 82 83 /* Needed nodes for linked list with installed ports. */ 84 INSTALLEDPORT *head = (INSTALLEDPORT *) NULL; 85 INSTALLEDPORT *curr = (INSTALLEDPORT *) NULL; 86 87 DIR *dir; 88 FILE *fd; 89 90 pkg_wrap(PKG_INSTALL_VERSION, argv); 91 92 while ((ch = getopt_long(argc, argv, opts, longopts, NULL)) != -1) { 93 switch (ch) { 94 case 'd': 95 dflag = 1; 96 date = optarg; 97 break; 98 case 'f': 99 updatingfile = optarg; 100 break; 101 case 'h': 102 default: 103 usage(); 104 } 105 } 106 argc -= optind; 107 argv += optind; 108 109 /* Check if passed date has a correct format. */ 110 if (dflag == 1) { 111 linelength = strlen(date); 112 if (linelength != 8) 113 exit(EX_DATAERR); 114 if (strspn(date, "0123456789") != 8) { 115 fprintf(stderr, "unknown date format: %s\n", date); 116 exit(EX_DATAERR); 117 } 118 } 119 120 /* Save the list of passed portnames. */ 121 if (argc != 0) { 122 pflag = 1; 123 while (*argv) { 124 if ((curr = (INSTALLEDPORT *) 125 malloc(sizeof(INSTALLEDPORT))) == NULL) 126 (void)exit(EXIT_FAILURE); 127 strlcpy(curr->name, *argv, strlen(*argv) + 1); 128 curr->next = head; 129 head = curr; 130 (void)*argv++; 131 } 132 } 133 134 /* 135 * UPDATING will be parsed for all installed ports 136 * if no portname is passed. 137 */ 138 if (pflag == 0) { 139 /* Open /var/db/pkg and search for all installed ports. */ 140 if ((dir = opendir(pkgdbpath)) != NULL) { 141 while ((pkgdbdir = readdir(dir)) != NULL) { 142 if (strcmp(pkgdbdir->d_name, ".") != 0 && 143 strcmp(pkgdbdir->d_name, "..") != 0) { 144 145 /* Create path to +CONTENTS file for each installed port */ 146 n = strlcpy(tmp_file, pkgdbpath, strlen(pkgdbpath)+1); 147 n = strlcpy(tmp_file + n, "/", sizeof(tmp_file) - n); 148 n = strlcat(tmp_file + n, pkgdbdir->d_name, 149 sizeof(tmp_file) - n); 150 if (stat(tmp_file, &attribute) == -1) { 151 fprintf(stderr, "can't open %s: %s\n", 152 tmp_file, strerror(errno)); 153 return EXIT_FAILURE; 154 } 155 if (attribute.st_mode & S_IFREG) 156 continue; 157 (void)strlcat(tmp_file + n, "/", 158 sizeof(tmp_file) - n); 159 (void)strlcat(tmp_file + n, CONTENTS_FNAME, 160 sizeof(tmp_file) - n); 161 162 /* Open +CONTENT file */ 163 fd = fopen(tmp_file, "r"); 164 if (fd == NULL) { 165 fprintf(stderr, "warning: can't open %s: %s\n", 166 tmp_file, strerror(errno)); 167 continue; 168 } 169 170 /* 171 * Parses +CONTENT for ORIGIN line and 172 * put element into linked list. 173 */ 174 while (fgets(originline, maxcharperline, fd) != NULL) { 175 tmpline1 = strstr(originline, origin); 176 if (tmpline1 != NULL) { 177 /* Tmp variable to store port name. */ 178 char *pname; 179 pname = strrchr(originline, (int)':'); 180 pname++; 181 if ((curr = (INSTALLEDPORT *) 182 malloc(sizeof(INSTALLEDPORT))) == NULL) 183 (void)exit(EXIT_FAILURE); 184 if (pname[strlen(pname) - 1] == '\n') 185 pname[strlen(pname) - 1] = '\0'; 186 strlcpy (curr->name, pname, strlen(pname)+1); 187 curr->next = head; 188 head = curr; 189 } 190 } 191 192 if (ferror(fd)) { 193 fprintf(stderr, "error reading input\n"); 194 exit(EX_IOERR); 195 } 196 197 (void)fclose(fd); 198 } 199 } 200 closedir(dir); 201 } 202 } 203 204 /* Fetch UPDATING file if needed and open file */ 205 if (isURL(updatingfile)) { 206 if ((fd = fetchGetURL(updatingfile, "")) == NULL) { 207 fprintf(stderr, "Error: Unable to get %s: %s\n", 208 updatingfile, fetchLastErrString); 209 exit(EX_UNAVAILABLE); 210 } 211 } 212 else { 213 fd = fopen(updatingfile, "r"); 214 } 215 if (fd == NULL) { 216 fprintf(stderr, "can't open %s: %s\n", 217 updatingfile, strerror(errno)); 218 exit(EX_UNAVAILABLE); 219 } 220 221 /* Parse opened UPDATING file. */ 222 while (fgets(updatingline, maxcharperline, fd) != NULL) { 223 /* No entry is found so far */ 224 if (found == 0) { 225 /* Search for AFFECTS line to parse the portname. */ 226 tmpline1 = strstr(updatingline, affects); 227 228 if (tmpline1 != NULL) { 229 curr = head; 230 while (curr != NULL) { 231 tmpline2 = strstr(updatingline, curr->name); 232 if (tmpline2 != NULL) 233 break; 234 curr = curr->next; 235 } 236 if (tmpline2 != NULL) { 237 /* If -d is set, check if entry is newer than the date. */ 238 if ((dflag == 1) && (strncmp(dateline, date, 8) < 0)) 239 continue; 240 printf("%s", dateline); 241 printf("%s", updatingline); 242 found = 1; 243 } 244 } 245 } 246 /* Search for the end of an entry, if not found print the line. */ 247 else { 248 tmpline1 = strstr(updatingline, end); 249 if (tmpline1 == NULL) 250 printf("%s", updatingline); 251 else { 252 linelength = strlen(updatingline); 253 if (linelength == 10) 254 found = 0; 255 else 256 printf("%s", updatingline); 257 } 258 } 259 /* Save the actual line, it could be a date. */ 260 dateline = strdup(updatingline); 261 } 262 263 if (ferror(fd)) { 264 fprintf(stderr, "error reading input\n"); 265 exit(EX_IOERR); 266 } 267 (void)fclose(fd); 268 269 exit(EX_OK); 270} 271 272int 273usage(void) 274{ 275 fprintf(stderr, 276 "usage: pkg_updating [-h] [-d YYYYMMDD] [-f file] [portname ...]\n"); 277 exit(EX_USAGE); 278} 279 280void 281cleanup(int sig) 282{ 283 if (sig) 284 exit(1); 285} 286