/mandos/trunk

To get this branch, use:
bzr branch http://bzr.recompile.se/loggerhead/mandos/trunk

« back to all changes in this revision

Viewing changes to plugins.d/mandos-client.c

  • Committer: Teddy Hogeborn
  • Date: 2014-07-25 22:44:20 UTC
  • mto: This revision was merged to the branch mainline in revision 724.
  • Revision ID: teddy@recompile.se-20140725224420-4a5ct2ptt0hsc92z
Require Python 2.7.

This is in preparation for the eventual move to Python 3, which will
happen as soon as all Python modules required by Mandos are available.
The mandos-ctl and mandos-monitor programs are already portable
between Python 2.6 and Python 3 without changes; this change will
bring the requirement up to Python 2.7.

* INSTALL (Prerequisites/Libraries/Mandos Server): Document
                                                   requirement of
                                                   Python 2.7; remove
                                                   Python-argparse
                                                   which is in the
                                                   Python 2.7 standard
                                                   library.
* debian/control (Source: mandos/Build-Depends-Indep): Depend on
                                                       exactly the
                                                       python2.7
                                                       package and all
                                                       the Python 2.7
                                                       versions of the
                                                       python modules.
  (Package: mandos/Depends): - '' - but still depend on python (<=2.7)
                            and the generic versions of the Python
                            modules; this is for mandos-ctl and
                            mandos-monitor, both of which are
                            compatible with Python 3, and use
                            #!/usr/bin/python.
* mandos: Use #!/usr/bin/python2.7 instead of #!/usr/bin/python.

Show diffs side-by-side

added added

removed removed

Lines of Context:
32
32
/* Needed by GPGME, specifically gpgme_data_seek() */
33
33
#ifndef _LARGEFILE_SOURCE
34
34
#define _LARGEFILE_SOURCE
35
 
#endif
 
35
#endif  /* not _LARGEFILE_SOURCE */
36
36
#ifndef _FILE_OFFSET_BITS
37
37
#define _FILE_OFFSET_BITS 64
38
 
#endif
 
38
#endif  /* not _FILE_OFFSET_BITS */
39
39
 
40
40
#define _GNU_SOURCE             /* TEMP_FAILURE_RETRY(), asprintf() */
41
41
 
42
42
#include <stdio.h>              /* fprintf(), stderr, fwrite(),
43
 
                                   stdout, ferror(), remove() */
 
43
                                   stdout, ferror() */
44
44
#include <stdint.h>             /* uint16_t, uint32_t, intptr_t */
45
45
#include <stddef.h>             /* NULL, size_t, ssize_t */
46
46
#include <stdlib.h>             /* free(), EXIT_SUCCESS, srand(),
57
57
#include <sys/socket.h>         /* socket(), struct sockaddr_in6,
58
58
                                   inet_pton(), connect(),
59
59
                                   getnameinfo() */
60
 
#include <fcntl.h>              /* open() */
 
60
#include <fcntl.h>              /* open(), unlinkat() */
61
61
#include <dirent.h>             /* opendir(), struct dirent, readdir()
62
62
                                 */
63
63
#include <inttypes.h>           /* PRIu16, PRIdMAX, intmax_t,
73
73
                                */
74
74
#include <unistd.h>             /* close(), SEEK_SET, off_t, write(),
75
75
                                   getuid(), getgid(), seteuid(),
76
 
                                   setgid(), pause(), _exit() */
 
76
                                   setgid(), pause(), _exit(),
 
77
                                   unlinkat() */
77
78
#include <arpa/inet.h>          /* inet_pton(), htons() */
78
79
#include <iso646.h>             /* not, or, and */
79
80
#include <argp.h>               /* struct argp_option, error_t, struct
141
142
static const char sys_class_net[] = "/sys/class/net";
142
143
char *connect_to = NULL;
143
144
const char *hookdir = HOOKDIR;
 
145
int hookdir_fd = -1;
144
146
uid_t uid = 65534;
145
147
gid_t gid = 65534;
146
148
 
232
234
                          .af = af };
233
235
  if(new_server->ip == NULL){
234
236
    perror_plus("strdup");
 
237
    free(new_server);
235
238
    return false;
236
239
  }
237
240
  ret = clock_gettime(CLOCK_MONOTONIC, &(new_server->last_seen));
238
241
  if(ret == -1){
239
242
    perror_plus("clock_gettime");
 
243
#ifdef __GNUC__
 
244
#pragma GCC diagnostic push
 
245
#pragma GCC diagnostic ignored "-Wcast-qual"
 
246
#endif
 
247
    free((char *)(new_server->ip));
 
248
#ifdef __GNUC__
 
249
#pragma GCC diagnostic pop
 
250
#endif
 
251
    free(new_server);
240
252
    return false;
241
253
  }
242
254
  /* Special case of first server */
1064
1076
     timed out */
1065
1077
  
1066
1078
  if(quit_now){
 
1079
    avahi_s_service_resolver_free(r);
1067
1080
    return;
1068
1081
  }
1069
1082
  
1335
1348
  sret = strspn(direntry->d_name, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1336
1349
                "abcdefghijklmnopqrstuvwxyz"
1337
1350
                "0123456789"
1338
 
                "_-");
 
1351
                "_.-");
1339
1352
  if((direntry->d_name)[sret] != '\0'){
1340
1353
    /* Contains non-allowed characters */
1341
1354
    if(debug){
1345
1358
    return 0;
1346
1359
  }
1347
1360
  
1348
 
  char *fullname = NULL;
1349
 
  ret = asprintf(&fullname, "%s/%s", hookdir, direntry->d_name);
1350
 
  if(ret < 0){
1351
 
    perror_plus("asprintf");
1352
 
    return 0;
1353
 
  }
1354
 
  
1355
 
  ret = stat(fullname, &st);
 
1361
  ret = fstatat(hookdir_fd, direntry->d_name, &st, 0);
1356
1362
  if(ret == -1){
1357
1363
    if(debug){
1358
1364
      perror_plus("Could not stat hook");
1463
1469
  error_t ret_errno = 0;
1464
1470
  if(seteuid(0) == -1){
1465
1471
    ret_errno = errno;
1466
 
    perror_plus("seteuid");
1467
1472
  }
1468
1473
  errno = old_errno;
1469
1474
  return ret_errno;
1480
1485
  }
1481
1486
  if(setuid(0) == -1){
1482
1487
    ret_errno = errno;
1483
 
    perror_plus("seteuid");
1484
1488
  }
1485
1489
  errno = old_errno;
1486
1490
  return ret_errno;
1493
1497
  error_t ret_errno = 0;
1494
1498
  if(seteuid(uid) == -1){
1495
1499
    ret_errno = errno;
1496
 
    perror_plus("seteuid");
1497
1500
  }
1498
1501
  errno = old_errno;
1499
1502
  return ret_errno;
1506
1509
  error_t ret_errno = 0;
1507
1510
  if(setuid(uid) == -1){
1508
1511
    ret_errno = errno;
1509
 
    perror_plus("setuid");
1510
1512
  }
1511
1513
  errno = old_errno;
1512
1514
  return ret_errno;
1515
1517
__attribute__((nonnull))
1516
1518
void run_network_hooks(const char *mode, const char *interface,
1517
1519
                       const float delay){
1518
 
  struct dirent **direntries;
1519
 
  int numhooks = scandir(hookdir, &direntries, runnable_hook,
1520
 
                         alphasort);
 
1520
  struct dirent **direntries = NULL;
 
1521
  if(hookdir_fd == -1){
 
1522
    hookdir_fd = open(hookdir, O_RDONLY);
 
1523
    if(hookdir_fd == -1){
 
1524
      if(errno == ENOENT){
 
1525
        if(debug){
 
1526
          fprintf_plus(stderr, "Network hook directory \"%s\" not"
 
1527
                       " found\n", hookdir);
 
1528
        }
 
1529
      } else {
 
1530
        perror_plus("open");
 
1531
      }
 
1532
      return;
 
1533
    }
 
1534
  }
 
1535
#ifdef __GLIBC__
 
1536
#if __GLIBC_PREREQ(2, 15)
 
1537
  int numhooks = scandirat(hookdir_fd, ".", &direntries,
 
1538
                           runnable_hook, alphasort);
 
1539
#else  /* not __GLIBC_PREREQ(2, 15) */
 
1540
  int numhooks = scandir(hookdir, &direntries, runnable_hook,
 
1541
                         alphasort);
 
1542
#endif  /* not __GLIBC_PREREQ(2, 15) */
 
1543
#else   /* not __GLIBC__ */
 
1544
  int numhooks = scandir(hookdir, &direntries, runnable_hook,
 
1545
                         alphasort);
 
1546
#endif  /* not __GLIBC__ */
1521
1547
  if(numhooks == -1){
1522
 
    if(errno == ENOENT){
1523
 
      if(debug){
1524
 
        fprintf_plus(stderr, "Network hook directory \"%s\" not"
1525
 
                     " found\n", hookdir);
1526
 
      }
1527
 
    } else {
1528
 
      perror_plus("scandir");
 
1548
    perror_plus("scandir");
 
1549
    return;
 
1550
  }
 
1551
  struct dirent *direntry;
 
1552
  int ret;
 
1553
  int devnull = open("/dev/null", O_RDONLY);
 
1554
  for(int i = 0; i < numhooks; i++){
 
1555
    direntry = direntries[i];
 
1556
    if(debug){
 
1557
      fprintf_plus(stderr, "Running network hook \"%s\"\n",
 
1558
                   direntry->d_name);
1529
1559
    }
1530
 
  } else {
1531
 
    struct dirent *direntry;
1532
 
    int ret;
1533
 
    int devnull = open("/dev/null", O_RDONLY);
1534
 
    for(int i = 0; i < numhooks; i++){
1535
 
      direntry = direntries[i];
1536
 
      char *fullname = NULL;
1537
 
      ret = asprintf(&fullname, "%s/%s", hookdir, direntry->d_name);
1538
 
      if(ret < 0){
 
1560
    pid_t hook_pid = fork();
 
1561
    if(hook_pid == 0){
 
1562
      /* Child */
 
1563
      /* Raise privileges */
 
1564
      errno = raise_privileges_permanently();
 
1565
      if(errno != 0){
 
1566
        perror_plus("Failed to raise privileges");
 
1567
        _exit(EX_NOPERM);
 
1568
      }
 
1569
      /* Set group */
 
1570
      errno = 0;
 
1571
      ret = setgid(0);
 
1572
      if(ret == -1){
 
1573
        perror_plus("setgid");
 
1574
        _exit(EX_NOPERM);
 
1575
      }
 
1576
      /* Reset supplementary groups */
 
1577
      errno = 0;
 
1578
      ret = setgroups(0, NULL);
 
1579
      if(ret == -1){
 
1580
        perror_plus("setgroups");
 
1581
        _exit(EX_NOPERM);
 
1582
      }
 
1583
      ret = dup2(devnull, STDIN_FILENO);
 
1584
      if(ret == -1){
 
1585
        perror_plus("dup2(devnull, STDIN_FILENO)");
 
1586
        _exit(EX_OSERR);
 
1587
      }
 
1588
      ret = close(devnull);
 
1589
      if(ret == -1){
 
1590
        perror_plus("close");
 
1591
        _exit(EX_OSERR);
 
1592
      }
 
1593
      ret = dup2(STDERR_FILENO, STDOUT_FILENO);
 
1594
      if(ret == -1){
 
1595
        perror_plus("dup2(STDERR_FILENO, STDOUT_FILENO)");
 
1596
        _exit(EX_OSERR);
 
1597
      }
 
1598
      ret = setenv("MANDOSNETHOOKDIR", hookdir, 1);
 
1599
      if(ret == -1){
 
1600
        perror_plus("setenv");
 
1601
        _exit(EX_OSERR);
 
1602
      }
 
1603
      ret = setenv("DEVICE", interface, 1);
 
1604
      if(ret == -1){
 
1605
        perror_plus("setenv");
 
1606
        _exit(EX_OSERR);
 
1607
      }
 
1608
      ret = setenv("VERBOSITY", debug ? "1" : "0", 1);
 
1609
      if(ret == -1){
 
1610
        perror_plus("setenv");
 
1611
        _exit(EX_OSERR);
 
1612
      }
 
1613
      ret = setenv("MODE", mode, 1);
 
1614
      if(ret == -1){
 
1615
        perror_plus("setenv");
 
1616
        _exit(EX_OSERR);
 
1617
      }
 
1618
      char *delaystring;
 
1619
      ret = asprintf(&delaystring, "%f", (double)delay);
 
1620
      if(ret == -1){
1539
1621
        perror_plus("asprintf");
1540
 
        continue;
1541
 
      }
1542
 
      if(debug){
1543
 
        fprintf_plus(stderr, "Running network hook \"%s\"\n",
1544
 
                     direntry->d_name);
1545
 
      }
1546
 
      pid_t hook_pid = fork();
1547
 
      if(hook_pid == 0){
1548
 
        /* Child */
1549
 
        /* Raise privileges */
1550
 
        if(raise_privileges_permanently() != 0){
1551
 
          perror_plus("Failed to raise privileges");
1552
 
          _exit(EX_NOPERM);
1553
 
        }
1554
 
        /* Set group */
1555
 
        errno = 0;
1556
 
        ret = setgid(0);
1557
 
        if(ret == -1){
1558
 
          perror_plus("setgid");
1559
 
          _exit(EX_NOPERM);
1560
 
        }
1561
 
        /* Reset supplementary groups */
1562
 
        errno = 0;
1563
 
        ret = setgroups(0, NULL);
1564
 
        if(ret == -1){
1565
 
          perror_plus("setgroups");
1566
 
          _exit(EX_NOPERM);
1567
 
        }
1568
 
        ret = dup2(devnull, STDIN_FILENO);
1569
 
        if(ret == -1){
1570
 
          perror_plus("dup2(devnull, STDIN_FILENO)");
1571
 
          _exit(EX_OSERR);
1572
 
        }
1573
 
        ret = close(devnull);
1574
 
        if(ret == -1){
1575
 
          perror_plus("close");
1576
 
          _exit(EX_OSERR);
1577
 
        }
1578
 
        ret = dup2(STDERR_FILENO, STDOUT_FILENO);
1579
 
        if(ret == -1){
1580
 
          perror_plus("dup2(STDERR_FILENO, STDOUT_FILENO)");
1581
 
          _exit(EX_OSERR);
1582
 
        }
1583
 
        ret = setenv("MANDOSNETHOOKDIR", hookdir, 1);
1584
 
        if(ret == -1){
1585
 
          perror_plus("setenv");
1586
 
          _exit(EX_OSERR);
1587
 
        }
1588
 
        ret = setenv("DEVICE", interface, 1);
1589
 
        if(ret == -1){
1590
 
          perror_plus("setenv");
1591
 
          _exit(EX_OSERR);
1592
 
        }
1593
 
        ret = setenv("VERBOSITY", debug ? "1" : "0", 1);
1594
 
        if(ret == -1){
1595
 
          perror_plus("setenv");
1596
 
          _exit(EX_OSERR);
1597
 
        }
1598
 
        ret = setenv("MODE", mode, 1);
1599
 
        if(ret == -1){
1600
 
          perror_plus("setenv");
1601
 
          _exit(EX_OSERR);
1602
 
        }
1603
 
        char *delaystring;
1604
 
        ret = asprintf(&delaystring, "%f", (double)delay);
1605
 
        if(ret == -1){
1606
 
          perror_plus("asprintf");
1607
 
          _exit(EX_OSERR);
1608
 
        }
1609
 
        ret = setenv("DELAY", delaystring, 1);
1610
 
        if(ret == -1){
1611
 
          free(delaystring);
1612
 
          perror_plus("setenv");
1613
 
          _exit(EX_OSERR);
1614
 
        }
 
1622
        _exit(EX_OSERR);
 
1623
      }
 
1624
      ret = setenv("DELAY", delaystring, 1);
 
1625
      if(ret == -1){
1615
1626
        free(delaystring);
1616
 
        if(connect_to != NULL){
1617
 
          ret = setenv("CONNECT", connect_to, 1);
1618
 
          if(ret == -1){
1619
 
            perror_plus("setenv");
1620
 
            _exit(EX_OSERR);
1621
 
          }
1622
 
        }
1623
 
        if(execl(fullname, direntry->d_name, mode, NULL) == -1){
1624
 
          perror_plus("execl");
1625
 
          _exit(EXIT_FAILURE);
1626
 
        }
 
1627
        perror_plus("setenv");
 
1628
        _exit(EX_OSERR);
 
1629
      }
 
1630
      free(delaystring);
 
1631
      if(connect_to != NULL){
 
1632
        ret = setenv("CONNECT", connect_to, 1);
 
1633
        if(ret == -1){
 
1634
          perror_plus("setenv");
 
1635
          _exit(EX_OSERR);
 
1636
        }
 
1637
      }
 
1638
      int hook_fd = openat(hookdir_fd, direntry->d_name, O_RDONLY);
 
1639
      if(hook_fd == -1){
 
1640
        perror_plus("openat");
 
1641
        _exit(EXIT_FAILURE);
 
1642
      }
 
1643
      if((int)TEMP_FAILURE_RETRY(close(hookdir_fd)) == -1){
 
1644
        perror_plus("close");
 
1645
        _exit(EXIT_FAILURE);
 
1646
      }
 
1647
      if(fexecve(hook_fd, (char *const []){ direntry->d_name, NULL },
 
1648
                 environ) == -1){
 
1649
        perror_plus("fexecve");
 
1650
        _exit(EXIT_FAILURE);
 
1651
      }
 
1652
    } else {
 
1653
      int status;
 
1654
      if(TEMP_FAILURE_RETRY(waitpid(hook_pid, &status, 0)) == -1){
 
1655
        perror_plus("waitpid");
 
1656
        free(direntry);
 
1657
        continue;
 
1658
      }
 
1659
      if(WIFEXITED(status)){
 
1660
        if(WEXITSTATUS(status) != 0){
 
1661
          fprintf_plus(stderr, "Warning: network hook \"%s\" exited"
 
1662
                       " with status %d\n", direntry->d_name,
 
1663
                       WEXITSTATUS(status));
 
1664
          free(direntry);
 
1665
          continue;
 
1666
        }
 
1667
      } else if(WIFSIGNALED(status)){
 
1668
        fprintf_plus(stderr, "Warning: network hook \"%s\" died by"
 
1669
                     " signal %d\n", direntry->d_name,
 
1670
                     WTERMSIG(status));
 
1671
        free(direntry);
 
1672
        continue;
1627
1673
      } else {
1628
 
        int status;
1629
 
        if(TEMP_FAILURE_RETRY(waitpid(hook_pid, &status, 0)) == -1){
1630
 
          perror_plus("waitpid");
1631
 
          free(fullname);
1632
 
          continue;
1633
 
        }
1634
 
        if(WIFEXITED(status)){
1635
 
          if(WEXITSTATUS(status) != 0){
1636
 
            fprintf_plus(stderr, "Warning: network hook \"%s\" exited"
1637
 
                         " with status %d\n", direntry->d_name,
1638
 
                         WEXITSTATUS(status));
1639
 
            free(fullname);
1640
 
            continue;
1641
 
          }
1642
 
        } else if(WIFSIGNALED(status)){
1643
 
          fprintf_plus(stderr, "Warning: network hook \"%s\" died by"
1644
 
                       " signal %d\n", direntry->d_name,
1645
 
                       WTERMSIG(status));
1646
 
          free(fullname);
1647
 
          continue;
1648
 
        } else {
1649
 
          fprintf_plus(stderr, "Warning: network hook \"%s\""
1650
 
                       " crashed\n", direntry->d_name);
1651
 
          free(fullname);
1652
 
          continue;
1653
 
        }
1654
 
      }
1655
 
      free(fullname);
1656
 
      if(debug){
1657
 
        fprintf_plus(stderr, "Network hook \"%s\" ran successfully\n",
1658
 
                     direntry->d_name);
1659
 
      }
1660
 
    }
1661
 
    close(devnull);
1662
 
  }
 
1674
        fprintf_plus(stderr, "Warning: network hook \"%s\""
 
1675
                     " crashed\n", direntry->d_name);
 
1676
        free(direntry);
 
1677
        continue;
 
1678
      }
 
1679
    }
 
1680
    if(debug){
 
1681
      fprintf_plus(stderr, "Network hook \"%s\" ran successfully\n",
 
1682
                   direntry->d_name);
 
1683
    }
 
1684
    free(direntry);
 
1685
  }
 
1686
  free(direntries);
 
1687
  if((int)TEMP_FAILURE_RETRY(close(hookdir_fd)) == -1){
 
1688
    perror_plus("close");
 
1689
  } else {
 
1690
    hookdir_fd = -1;
 
1691
  }
 
1692
  close(devnull);
1663
1693
}
1664
1694
 
1665
1695
__attribute__((nonnull, warn_unused_result))
1716
1746
    /* Raise privileges */
1717
1747
    ret_errno = raise_privileges();
1718
1748
    if(ret_errno != 0){
 
1749
      errno = ret_errno;
1719
1750
      perror_plus("Failed to raise privileges");
1720
1751
    }
1721
1752
    
1825
1856
    /* Raise privileges */
1826
1857
    ret_errno = raise_privileges();
1827
1858
    if(ret_errno != 0){
 
1859
      errno = ret_errno;
1828
1860
      perror_plus("Failed to raise privileges");
1829
1861
    }
1830
1862
    
2237
2269
  
2238
2270
  /* If no interfaces were specified, make a list */
2239
2271
  if(mc.interfaces == NULL){
2240
 
    struct dirent **direntries;
 
2272
    struct dirent **direntries = NULL;
2241
2273
    /* Look for any good interfaces */
2242
2274
    ret = scandir(sys_class_net, &direntries, good_interface,
2243
2275
                  alphasort);
2249
2281
        if(ret_errno != 0){
2250
2282
          errno = ret_errno;
2251
2283
          perror_plus("argz_add");
 
2284
          free(direntries[i]);
2252
2285
          continue;
2253
2286
        }
2254
2287
        if(debug){
2255
2288
          fprintf_plus(stderr, "Will use interface \"%s\"\n",
2256
2289
                       direntries[i]->d_name);
2257
2290
        }
 
2291
        free(direntries[i]);
2258
2292
      }
2259
2293
      free(direntries);
2260
2294
    } else {
2261
 
      free(direntries);
 
2295
      if(ret == 0){
 
2296
        free(direntries);
 
2297
      }
2262
2298
      fprintf_plus(stderr, "Could not find a network interface\n");
2263
2299
      exitcode = EXIT_FAILURE;
2264
2300
      goto end;
2486
2522
  if(debug){
2487
2523
    fprintf_plus(stderr, "Starting Avahi loop search\n");
2488
2524
  }
2489
 
 
 
2525
  
2490
2526
  ret = avahi_loop_with_timeout(simple_poll,
2491
2527
                                (int)(retry_interval * 1000), &mc);
2492
2528
  if(debug){
2528
2564
    mc.current_server->prev->next = NULL;
2529
2565
    while(mc.current_server != NULL){
2530
2566
      server *next = mc.current_server->next;
 
2567
#ifdef __GNUC__
 
2568
#pragma GCC diagnostic push
 
2569
#pragma GCC diagnostic ignored "-Wcast-qual"
 
2570
#endif
 
2571
      free((char *)(mc.current_server->ip));
 
2572
#ifdef __GNUC__
 
2573
#pragma GCC diagnostic pop
 
2574
#endif
2531
2575
      free(mc.current_server);
2532
2576
      mc.current_server = next;
2533
2577
    }
2537
2581
  {
2538
2582
    ret_errno = raise_privileges();
2539
2583
    if(ret_errno != 0){
 
2584
      errno = ret_errno;
2540
2585
      perror_plus("Failed to raise privileges");
2541
2586
    } else {
2542
2587
      
2565
2610
    
2566
2611
    ret_errno = lower_privileges_permanently();
2567
2612
    if(ret_errno != 0){
 
2613
      errno = ret_errno;
2568
2614
      perror_plus("Failed to lower privileges permanently");
2569
2615
    }
2570
2616
  }
2575
2621
  /* Removes the GPGME temp directory and all files inside */
2576
2622
  if(tempdir != NULL){
2577
2623
    struct dirent **direntries = NULL;
2578
 
    struct dirent *direntry = NULL;
2579
 
    int numentries = scandir(tempdir, &direntries, notdotentries,
2580
 
                             alphasort);
2581
 
    if(numentries > 0){
2582
 
      for(int i = 0; i < numentries; i++){
2583
 
        direntry = direntries[i];
2584
 
        char *fullname = NULL;
2585
 
        ret = asprintf(&fullname, "%s/%s", tempdir,
2586
 
                       direntry->d_name);
2587
 
        if(ret < 0){
2588
 
          perror_plus("asprintf");
2589
 
          continue;
2590
 
        }
2591
 
        ret = remove(fullname);
2592
 
        if(ret == -1){
2593
 
          fprintf_plus(stderr, "remove(\"%s\"): %s\n", fullname,
2594
 
                       strerror(errno));
2595
 
        }
2596
 
        free(fullname);
 
2624
    int tempdir_fd = (int)TEMP_FAILURE_RETRY(open(tempdir, O_RDONLY |
 
2625
                                                  O_NOFOLLOW));
 
2626
    if(tempdir_fd == -1){
 
2627
      perror_plus("open");
 
2628
    } else {
 
2629
#ifdef __GLIBC__
 
2630
#if __GLIBC_PREREQ(2, 15)
 
2631
      int numentries = scandirat(tempdir_fd, ".", &direntries,
 
2632
                                 notdotentries, alphasort);
 
2633
#else  /* not __GLIBC_PREREQ(2, 15) */
 
2634
      int numentries = scandir(tempdir, &direntries, notdotentries,
 
2635
                               alphasort);
 
2636
#endif  /* not __GLIBC_PREREQ(2, 15) */
 
2637
#else   /* not __GLIBC__ */
 
2638
      int numentries = scandir(tempdir, &direntries, notdotentries,
 
2639
                               alphasort);
 
2640
#endif  /* not __GLIBC__ */
 
2641
      if(numentries >= 0){
 
2642
        for(int i = 0; i < numentries; i++){
 
2643
          ret = unlinkat(tempdir_fd, direntries[i]->d_name, 0);
 
2644
          if(ret == -1){
 
2645
            fprintf_plus(stderr, "unlinkat(open(\"%s\", O_RDONLY),"
 
2646
                         " \"%s\", 0): %s\n", tempdir,
 
2647
                         direntries[i]->d_name, strerror(errno));
 
2648
          }
 
2649
          free(direntries[i]);
 
2650
        }
 
2651
        
 
2652
        /* need to clean even if 0 because man page doesn't specify */
 
2653
        free(direntries);
 
2654
        if(numentries == -1){
 
2655
          perror_plus("scandir");
 
2656
        }
 
2657
        ret = rmdir(tempdir);
 
2658
        if(ret == -1 and errno != ENOENT){
 
2659
          perror_plus("rmdir");
 
2660
        }
2597
2661
      }
2598
 
    }
2599
 
 
2600
 
    /* need to clean even if 0 because man page doesn't specify */
2601
 
    free(direntries);
2602
 
    if(numentries == -1){
2603
 
      perror_plus("scandir");
2604
 
    }
2605
 
    ret = rmdir(tempdir);
2606
 
    if(ret == -1 and errno != ENOENT){
2607
 
      perror_plus("rmdir");
 
2662
      TEMP_FAILURE_RETRY(close(tempdir_fd));
2608
2663
    }
2609
2664
  }
2610
2665