aboutsummaryrefslogtreecommitdiff
path: root/usr.sbin/pkg_install/updating/main.c
blob: 993ccd52310413d559d1f70cab0053f23063b671 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
/*-
 * ----------------------------------------------------------------------------
 * "THE BEER-WARE LICENSE" (Revision 42):
 * <beat@chruetertee.ch> 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 <sys/cdefs.h>
__FBSDID("$FreeBSD$");


#include <sys/param.h>
#include <stdio.h>
#include <errno.h>
#include <fetch.h>
#include <limits.h>
#include <sysexits.h>
#include <getopt.h>

#include <pkg.h>
#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);

static char opts[] = "d:f:h";
static struct option longopts[] = {
	{ "date",	required_argument,	NULL,		'd' },
	{ "file",	required_argument,	NULL,		'f' },
	{ "help",	no_argument,		NULL,		'h' },
	{ NULL,		0,			NULL,		0 },
};

/*
 * 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;

	pkg_wrap(PKG_INSTALL_VERSION, argv);

	while ((ch = getopt_long(argc, argv, opts, longopts, NULL)) != -1) {
		switch (ch) {
			case 'd':
				dflag = 1;
				date = optarg;
				break;
			case 'f':
				updatingfile = optarg;
				break;
			case 'h':
			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, "/",
						sizeof(tmp_file) - n);
					(void)strlcat(tmp_file + n, CONTENTS_FNAME,
						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);
							if (pname[strlen(pname) - 1] == '\n')
								pname[strlen(pname) - 1] = '\0';
							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);
		} 
	}

	/* Fetch UPDATING file if needed and open file */
	if (isURL(updatingfile)) {
		if ((fd = fetchGetURL(updatingfile, "")) == NULL) {
			fprintf(stderr, "Error: Unable to get %s: %s\n",
				updatingfile, fetchLastErrString);
			exit(EX_UNAVAILABLE);
		}
	}
	else {
		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 [-h] [-d YYYYMMDD] [-f file] [portname ...]\n");
	exit(EX_USAGE);
}

void
cleanup(int sig)
{
	if (sig)
		exit(1);
}