]> sourceware.org Git - newlib-cygwin.git/blob - winsup/utils/dump_setup.cc
ChangeLog:
[newlib-cygwin.git] / winsup / utils / dump_setup.cc
1 /* dump_setup.cc
2
3 Copyright 2001, 2002, 2003, 2004, 2005, 2008, 2010 Red Hat, Inc.
4
5 This file is part of Cygwin.
6
7 This software is a copyrighted work licensed under the terms of the
8 Cygwin license. Please consult the file "CYGWIN_LICENSE" for
9 details. */
10
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <ctype.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <io.h>
17 #include <sys/stat.h>
18 #include <errno.h>
19 #define WIN32_NO_STATUS /* Disable status codes in winnt.h since we include
20 ntstatus.h for extended status codes below. */
21 #include <windows.h>
22 #undef WIN32_NO_STATUS
23 #ifndef __MINGW64_VERSION_MAJOR
24 # include <ddk/ntapi.h>
25 # include <ddk/winddk.h>
26 #else
27 # include <winternl.h>
28 # include <ntstatus.h>
29 #endif
30 #include "path.h"
31 #include <zlib.h>
32
33 static int package_len = 20;
34 static unsigned int version_len = 10;
35
36
37 typedef struct
38 {
39 char pkgtar[MAX_PATH + 1];
40 char pkg[MAX_PATH + 1];
41 char ver[MAX_PATH + 1];
42 char tail[MAX_PATH + 1];
43 char what[16];
44 } fileparse;
45
46 static int
47 find_tar_ext (const char *path)
48 {
49 char *p = strchr (path, '\0') - 7;
50 if (p <= path)
51 return 0;
52 if (*p == '.')
53 {
54 if (strcmp (p, ".tar.gz") != 0)
55 return 0;
56 }
57 else if (--p <= path || strcmp (p, ".tar.bz2") != 0)
58 return 0;
59
60 return p - path;
61 }
62
63 static char *
64 base (const char *s)
65 {
66 if (!s)
67 return 0;
68 const char *rv = s;
69 while (*s)
70 {
71 if ((*s == '/' || *s == ':' || *s == '\\') && s[1])
72 rv = s + 1;
73 s++;
74 }
75 return (char *) rv;
76 }
77
78 /* Parse a filename into package, version, and extension components. */
79 int
80 parse_filename (const char *in_fn, fileparse& f)
81 {
82 char *p, *ver;
83 char fn[strlen (in_fn) + 1];
84
85 strcpy (fn, in_fn);
86 int n = find_tar_ext (fn);
87
88 if (!n)
89 return 0;
90
91 strcpy (f.tail, fn + n);
92 fn[n] = '\0';
93 f.pkg[0] = f.what[0] = '\0';
94 p = base (fn);
95 for (ver = p; *ver; ver++)
96 if (*ver != '-')
97 continue;
98 else if (isdigit (ver[1]))
99 {
100 *ver++ = '\0';
101 strcpy (f.pkg, p);
102 break;
103 }
104 else if (strcasecmp (ver, "-src") == 0 ||
105 strcasecmp (ver, "-patch") == 0)
106 {
107 *ver++ = '\0';
108 strcpy (f.pkg, p);
109 strcpy (f.what, strlwr (ver));
110 strcpy (f.pkgtar, p);
111 strcat (f.pkgtar, f.tail);
112 ver = strchr (ver, '\0');
113 break;
114 }
115
116 if (!f.pkg[0])
117 strcpy (f.pkg, p);
118
119 if (!f.what[0])
120 {
121 int n;
122 p = strchr (ver, '\0');
123 strcpy (f.pkgtar, in_fn);
124 if ((p -= 4) >= ver && strcasecmp (p, "-src") == 0)
125 n = 4;
126 else if ((p -= 2) >= ver && strcasecmp (p, "-patch") == 0)
127 n = 6;
128 else
129 n = 0;
130 if (n)
131 {
132 strcpy (f.what, p + 1);
133 *p = '\0';
134 p = f.pkgtar + (p - fn) + n;
135 memmove (p - 4, p, strlen (p));
136 }
137 }
138
139 strcpy (f.ver, *ver ? ver : "0.0");
140 return 1;
141 }
142
143 static bool
144 dump_file (const char *msg, const char *fn)
145 {
146 char buf[4096];
147 bool printed = false;
148 bool found = false;
149 size_t len = strlen (fn);
150 char *path = cygpath ("/etc/setup/setup.rc", NULL);
151 FILE *fp = fopen (path, "rt");
152
153 if (fp)
154 {
155 while (fgets (buf, 4096, fp))
156 {
157 if (found)
158 {
159 char *bufp = buf;
160
161 if (*bufp == '\t')
162 ++bufp;
163 char *p = strchr (bufp, '\0');
164 printf ("%s%s%s", msg, bufp,
165 (p == bufp) || p[-1] != '\n' ? "\n" : "");
166 printed = true;
167 break;
168 }
169 if (!strncmp (buf, fn, len) && buf[len] == '\n')
170 found = true;
171 }
172 fclose (fp);
173 }
174 return printed;
175 }
176
177 struct pkgver
178 {
179 char *name;
180 char *ver;
181 };
182
183 extern "C" {
184 int
185 compar (const void *a, const void *b)
186 {
187 const pkgver *pa = (const pkgver *) a;
188 const pkgver *pb = (const pkgver *) b;
189 return strcasecmp (pa->name, pb->name);
190 }
191 }
192
193 int
194 match_argv (char **argv, const char *name)
195 {
196 if (!argv || !*argv)
197 return -1;
198 for (char **a = argv; *a; a++)
199 if (strcasecmp (*a, name) == 0)
200 return a - argv + 1;
201 return 0;
202 }
203
204 static bool
205 could_not_access (int verbose, char *filename, char *package, const char *type)
206 {
207 switch (errno)
208 {
209 case ENOTDIR:
210 break;
211 case ENOENT:
212 if (verbose)
213 printf ("Missing %s: /%s from package %s\n",
214 type, filename, package);
215 return true;
216 case EACCES:
217 if (verbose)
218 printf ("Unable to access %s /%s from package %s\n",
219 type, filename, package);
220 return true;
221 }
222 return false;
223 }
224
225 static const WCHAR tfx_chars[] = {
226 0, 0xf000 | 1, 0xf000 | 2, 0xf000 | 3,
227 0xf000 | 4, 0xf000 | 5, 0xf000 | 6, 0xf000 | 7,
228 0xf000 | 8, 0xf000 | 9, 0xf000 | 10, 0xf000 | 11,
229 0xf000 | 12, 0xf000 | 13, 0xf000 | 14, 0xf000 | 15,
230 0xf000 | 16, 0xf000 | 17, 0xf000 | 18, 0xf000 | 19,
231 0xf000 | 20, 0xf000 | 21, 0xf000 | 22, 0xf000 | 23,
232 0xf000 | 24, 0xf000 | 25, 0xf000 | 26, 0xf000 | 27,
233 0xf000 | 28, 0xf000 | 29, 0xf000 | 30, 0xf000 | 31,
234 ' ', '!', 0xf000 | '"', '#',
235 '$', '%', '&', 39,
236 '(', ')', 0xf000 | '*', '+',
237 ',', '-', '.', '\\',
238 '0', '1', '2', '3',
239 '4', '5', '6', '7',
240 '8', '9', 0xf000 | ':', ';',
241 0xf000 | '<', '=', 0xf000 | '>', 0xf000 | '?',
242 '@', 'A', 'B', 'C',
243 'D', 'E', 'F', 'G',
244 'H', 'I', 'J', 'K',
245 'L', 'M', 'N', 'O',
246 'P', 'Q', 'R', 'S',
247 'T', 'U', 'V', 'W',
248 'X', 'Y', 'Z', '[',
249 '\\', ']', '^', '_',
250 '`', 'a', 'b', 'c',
251 'd', 'e', 'f', 'g',
252 'h', 'i', 'j', 'k',
253 'l', 'm', 'n', 'o',
254 'p', 'q', 'r', 's',
255 't', 'u', 'v', 'w',
256 'x', 'y', 'z', '{',
257 0xf000 | '|', '}', '~', 127
258 };
259
260 static void
261 transform_chars (PWCHAR path, PWCHAR path_end)
262 {
263 for (; path <= path_end; ++path)
264 if (*path < 128)
265 *path = tfx_chars[*path];
266 }
267
268 extern "C" NTAPI NTSTATUS NtQueryAttributesFile (POBJECT_ATTRIBUTES,
269 PFILE_BASIC_INFORMATION);
270
271 /* This function checks for file existance and fills the stat structure
272 with only the required mode info. We're using a native NT function
273 here, otherwise we wouldn't be able to check for files with special
274 characters not valid in Win32, and espacially not valid using the
275 ANSI API. */
276 static int
277 simple_nt_stat (const char *filename, struct stat *st)
278 {
279 size_t len = mbstowcs (NULL, filename, 0) + 1;
280 WCHAR path[len + 8]; /* Enough space for the NT prefix */
281 PWCHAR p = path;
282 UNICODE_STRING upath;
283 OBJECT_ATTRIBUTES attr;
284 FILE_BASIC_INFORMATION fbi;
285 NTSTATUS status;
286
287 wcscpy (p, L"\\??\\");
288 p += 4;
289 if (filename[0] == '\\' && filename[1] == '\\')
290 {
291 wcscpy (p, L"UNC");
292 p += 3;
293 p += mbstowcs (p, filename + 1, len);
294 }
295 else
296 p += mbstowcs (p, filename, len);
297 /* Remove trailing backslashes. NT functions don't like them. */
298 if (p[-1] == L'\\')
299 *--p = L'\0';
300 /* Skip prefix and drive, otherwise question marks and colons are converted
301 as well. */
302 transform_chars (path + 7, p);
303 RtlInitUnicodeString (&upath, path);
304 InitializeObjectAttributes (&attr, &upath, OBJ_CASE_INSENSITIVE, NULL, NULL);
305 status = NtQueryAttributesFile (&attr, &fbi);
306 if (NT_SUCCESS (status))
307 {
308 st->st_mode = (fbi.FileAttributes & FILE_ATTRIBUTE_DIRECTORY)
309 ? S_IFDIR : S_IFREG;
310 return 0;
311 }
312 if (status == STATUS_OBJECT_PATH_NOT_FOUND
313 || status == STATUS_OBJECT_NAME_INVALID
314 || status == STATUS_BAD_NETWORK_PATH
315 || status == STATUS_BAD_NETWORK_NAME
316 || status == STATUS_NO_MEDIA_IN_DEVICE
317 || status == STATUS_OBJECT_NAME_NOT_FOUND
318 || status == STATUS_NO_SUCH_FILE)
319 errno = ENOENT;
320 else
321 errno = EACCES;
322 return -1;
323 }
324
325 static bool
326 directory_exists (int verbose, char *filename, char *package)
327 {
328 struct stat status;
329 if (simple_nt_stat(cygpath("/", filename, NULL), &status))
330 {
331 if (could_not_access (verbose, filename, package, "directory"))
332 return false;
333 }
334 else if (!S_ISDIR(status.st_mode))
335 {
336 if (verbose)
337 printf ("Directory/file mismatch: /%s from package %s\n", filename, package);
338 return false;
339 }
340 return true;
341 }
342
343 static bool
344 file_exists (int verbose, char *filename, const char *alt, char *package)
345 {
346 struct stat status;
347 if (simple_nt_stat(cygpath("/", filename, NULL), &status) &&
348 (!alt || simple_nt_stat(cygpath("/", filename, alt, NULL), &status)))
349 {
350 if (could_not_access (verbose, filename, package, "file"))
351 return false;
352 }
353 else if (!S_ISREG(status.st_mode))
354 {
355 if (verbose)
356 printf ("File type mismatch: /%s from package %s\n", filename, package);
357 return false;
358 }
359 return true;
360 }
361
362 static gzFile
363 open_package_list (char *package)
364 {
365 char filelist[MAX_PATH + 1] = "/etc/setup/";
366 strcat (strcat (filelist, package), ".lst.gz");
367 if (!file_exists (false, filelist + 1, NULL, NULL))
368 return NULL;
369
370 gzFile fp;
371 #ifndef ZLIB_VERSION
372 fp = NULL;
373 #else
374 char *fn = cygpath (filelist, NULL);
375 fp = gzopen (fn, "rb9");
376 free (fn);
377 #endif
378
379 return fp;
380 }
381
382 static bool
383 check_package_files (int verbose, char *package)
384 {
385 gzFile fp = open_package_list (package);
386 if (!fp)
387 {
388 if (verbose)
389 printf ("Empty package %s\n", package);
390 return true;
391 }
392
393 bool result = true;
394 char buf[MAX_PATH + 1];
395 while (gzgets (fp, buf, MAX_PATH))
396 {
397 char *filename = strtok(buf, "\n");
398
399 if (*filename == '/')
400 ++filename;
401 else if (!strncmp (filename, "./", 2))
402 filename += 2;
403
404 if (filename[strlen (filename) - 1] == '/')
405 {
406 if (!directory_exists (verbose, filename, package))
407 result = false;
408 }
409 else if (!strncmp (filename, "etc/postinstall/", 16))
410 {
411 if (!file_exists (verbose, filename, ".done", package))
412 result = false;
413 }
414 else
415 {
416 if (!file_exists (verbose, filename, ".lnk", package))
417 result = false;
418 }
419 }
420
421 gzclose (fp);
422 return result;
423 }
424
425 /**
426 * Returns a calloc'd sorted list of packages or NULL if no info.
427 * The last entry in the list is {NULL,NULL}.
428 */
429 static pkgver *
430 get_packages (char **argv)
431 {
432 char *setup = cygpath ("/etc/setup/installed.db", NULL);
433 FILE *fp = fopen (setup, "rt");
434
435 if (fp == NULL)
436 return NULL;
437
438 int nlines;
439 nlines = 0;
440 char buf[4096];
441 while (fgets (buf, 4096, fp))
442 nlines += 2; /* potentially binary + source */
443 if (!nlines)
444 {
445 fclose (fp);
446 return NULL;
447 }
448 rewind (fp);
449
450 pkgver *packages;
451
452 packages = (pkgver *) calloc (nlines + 1, sizeof(packages[0]));
453 int n;
454 for (n = 0; fgets (buf, 4096, fp) && n < nlines;)
455 {
456 char *package = strtok (buf, " ");
457 if (!package || !*package || !match_argv (argv, package))
458 continue;
459 for (int i = 0; i < 2; i++)
460 {
461 fileparse f;
462 char *tar = strtok (NULL, " ");
463 if (!tar || !*tar || !parse_filename (tar, f))
464 break;
465
466 int len = strlen (package);
467 if (f.what[0])
468 len += strlen (f.what) + 1;
469 if (len > package_len)
470 package_len = len;
471 packages[n].name = (char *) malloc (len + 1);
472 strcpy (packages[n].name, package);
473 if (f.what[0])
474 strcat (strcat (packages[n].name, "-"), f.what);
475 packages[n].ver = strdup (f.ver);
476 if (strlen(f.ver) > version_len)
477 version_len = strlen(f.ver);
478 n++;
479 if (strtok (NULL, " ") == NULL)
480 break;
481 }
482 }
483
484 packages[n].name = packages[n].ver = NULL;
485
486 qsort (packages, n, sizeof (packages[0]), compar);
487
488 fclose (fp);
489
490 return packages;
491 }
492
493 void
494 dump_setup (int verbose, char **argv, bool check_files)
495 {
496 pkgver *packages = get_packages(argv);
497
498 puts ("Cygwin Package Information");
499 if (packages == NULL)
500 {
501 puts ("No setup information found");
502 return;
503 }
504
505 if (verbose)
506 {
507 bool need_nl = dump_file ("Last downloaded files to: ", "last-cache");
508 if (dump_file ("Last downloaded files from: ", "last-mirror") || need_nl)
509 puts ("");
510 }
511
512 printf ("%-*s %-*s%s\n", package_len, "Package",
513 check_files ? version_len : 7, "Version",
514 check_files ? " Status" : "");
515 for (int i = 0; packages[i].name; i++)
516 {
517 if (check_files)
518 printf ("%-*s %-*s%s\n", package_len, packages[i].name,
519 version_len, packages[i].ver,
520 check_package_files (verbose, packages[i].name)
521 ? " OK" : " Incomplete");
522 else
523 printf ("%-*s %s\n", package_len, packages[i].name, packages[i].ver);
524 fflush(stdout);
525 }
526
527 free (packages);
528
529 return;
530 }
531
532 void
533 package_list (int verbose, char **argv)
534 {
535 pkgver *packages = get_packages(argv);
536 if (packages == NULL)
537 {
538 puts ("No setup information found");
539 return;
540 }
541
542 for (int i = 0; packages[i].name; i++)
543 {
544 gzFile fp = open_package_list (packages[i].name);
545 if (!fp)
546 {
547 if (verbose)
548 printf ("Can't open file list /etc/setup/%s.lst.gz for package %s\n",
549 packages[i].name, packages[i].name);
550 continue;
551 }
552
553 if (verbose)
554 printf ("Package: %s-%s\n", packages[i].name, packages[i].ver);
555
556 char buf[MAX_PATH + 1];
557 while (gzgets (fp, buf, MAX_PATH))
558 {
559 char *lastchar = strchr(buf, '\n');
560 if (lastchar[-1] != '/')
561 printf ("%s/%s", (verbose?" ":""), buf);
562 }
563
564 gzclose (fp);
565 }
566
567 free (packages);
568
569 return;
570 }
571
572 void
573 package_find (int verbose, char **argv)
574 {
575 pkgver *packages = get_packages(NULL);
576 if (packages == NULL)
577 {
578 puts ("No setup information found");
579 return;
580 }
581
582 for (int i = 0; packages[i].name; i++)
583 {
584 gzFile fp = open_package_list (packages[i].name);
585 if (!fp)
586 continue;
587
588 char buf[MAX_PATH + 2];
589 buf[0] = '/';
590 while (gzgets (fp, buf + 1, MAX_PATH))
591 {
592 char *filename = strtok(buf, "\n");
593 int flen = strlen (filename);
594 if (filename[flen - 1] != '/')
595 {
596 // FIXME: verify that /bin is mounted on /usr/bin; ditto for /lib
597 bool is_alias = !strncmp(filename, "/usr/bin/", 9) ||
598 !strncmp(filename, "/usr/lib/", 9);
599 int a = match_argv (argv, filename);
600 if (!a && is_alias)
601 a = match_argv (argv, filename + 4);
602 if (!a && !strcmp(filename + flen - 4, ".exe"))
603 {
604 filename[flen - 4] = '\0';
605 a = match_argv (argv, filename);
606 }
607 if (!a && is_alias)
608 a = match_argv (argv, filename + 4);
609 if (a > 0)
610 {
611 if (verbose)
612 printf ("%s: found in package ", filename);
613 printf ("%s-%s\n", packages[i].name, packages[i].ver);
614 }
615 }
616 }
617
618 gzclose (fp);
619 }
620
621 free (packages);
622
623 return;
624 }
625
This page took 0.060426 seconds and 5 git commands to generate.