pen.c revision 194497
1193323Sed/*
2193323Sed * FreeBSD install - a package for the installation and maintainance
3193323Sed * of non-core utilities.
4193323Sed *
5193323Sed * Redistribution and use in source and binary forms, with or without
6193323Sed * modification, are permitted provided that the following conditions
7193323Sed * are met:
8193323Sed * 1. Redistributions of source code must retain the above copyright
9193323Sed *    notice, this list of conditions and the following disclaimer.
10193323Sed * 2. Redistributions in binary form must reproduce the above copyright
11193323Sed *    notice, this list of conditions and the following disclaimer in the
12193323Sed *    documentation and/or other materials provided with the distribution.
13193323Sed *
14193323Sed * Jordan K. Hubbard
15193323Sed * 18 July 1993
16193323Sed *
17193323Sed * Routines for managing the "play pen".
18193323Sed *
19193323Sed */
20193323Sed
21193323Sed#include <sys/cdefs.h>
22193323Sed__FBSDID("$FreeBSD: head/usr.sbin/pkg_install/lib/pen.c 194497 2009-06-19 17:07:38Z brian $");
23193323Sed
24193323Sed#include "lib.h"
25193323Sed#include <err.h>
26193323Sed#include <libutil.h>
27193323Sed#include <libgen.h>
28193323Sed#include <sys/signal.h>
29193323Sed#include <sys/param.h>
30193323Sed#include <sys/mount.h>
31193323Sed
32193323Sed/* For keeping track of where we are */
33193323Sedstatic char PenLocation[FILENAME_MAX];
34193323Sed
35193323Sedchar *
36193323Sedwhere_playpen(void)
37193323Sed{
38193323Sed    return PenLocation;
39193323Sed}
40193323Sed
41193323Sed/* Find a good place to play. */
42193323Sedstatic char *
43193323Sedfind_play_pen(char *pen, off_t sz)
44193323Sed{
45193323Sed    char *cp;
46193323Sed    struct stat sb;
47193323Sed    char humbuf[6];
48193323Sed
49193323Sed    if (pen[0] && isdir(dirname(pen)) == TRUE && (min_free(dirname(pen)) >= sz))
50198090Srdivacky	return pen;
51193323Sed    else if ((cp = getenv("PKG_TMPDIR")) != NULL && stat(cp, &sb) != FAIL && (min_free(cp) >= sz))
52193323Sed	sprintf(pen, "%s/instmp.XXXXXX", cp);
53193323Sed    else if ((cp = getenv("TMPDIR")) != NULL && stat(cp, &sb) != FAIL && (min_free(cp) >= sz))
54198090Srdivacky	sprintf(pen, "%s/instmp.XXXXXX", cp);
55193323Sed    else if (stat("/var/tmp", &sb) != FAIL && min_free("/var/tmp") >= sz)
56193323Sed	strcpy(pen, "/var/tmp/instmp.XXXXXX");
57198090Srdivacky    else if (stat("/tmp", &sb) != FAIL && min_free("/tmp") >= sz)
58198090Srdivacky	strcpy(pen, "/tmp/instmp.XXXXXX");
59193323Sed    else if ((stat("/usr/tmp", &sb) == SUCCESS || mkdir("/usr/tmp", 01777) == SUCCESS) && min_free("/usr/tmp") >= sz)
60198090Srdivacky	strcpy(pen, "/usr/tmp/instmp.XXXXXX");
61193323Sed    else {
62193323Sed	cleanup(0);
63198090Srdivacky	humanize_number(humbuf, sizeof humbuf, sz, "", HN_AUTOSCALE,
64198090Srdivacky	    HN_NOSPACE);
65193323Sed	errx(2,
66193323Sed"%s: can't find enough temporary space to extract the files, please set your\n"
67193323Sed"PKG_TMPDIR environment variable to a location with at least %s bytes\n"
68193323Sed"free", __func__, humbuf);
69193323Sed	return NULL;
70193323Sed    }
71193323Sed    return pen;
72193323Sed}
73193323Sed
74193323Sed#define MAX_STACK	20
75193323Sedstatic char *pstack[MAX_STACK];
76193323Sedstatic int pdepth = -1;
77193323Sed
78193323Sedstatic const char *
79193323SedpushPen(const char *pen)
80193323Sed{
81193323Sed    if (++pdepth == MAX_STACK)
82193323Sed	errx(2, "%s: stack overflow.\n", __func__);
83193323Sed    pstack[pdepth] = strdup(pen);
84193323Sed
85193323Sed    return pstack[pdepth];
86193323Sed}
87193323Sed
88193323Sedstatic void
89193323SedpopPen(char *pen)
90193323Sed{
91193323Sed    if (pdepth == -1) {
92193323Sed	pen[0] = '\0';
93193323Sed	return;
94193323Sed    }
95193323Sed    strcpy(pen, pstack[pdepth]);
96193323Sed    free(pstack[pdepth--]);
97193323Sed}
98193323Sed
99193323Sed/*
100193323Sed * Make a temporary directory to play in and chdir() to it, returning
101193323Sed * pathname of previous working directory.
102193323Sed */
103193323Sedconst char *
104193323Sedmake_playpen(char *pen, off_t sz)
105193323Sed{
106193323Sed    char humbuf1[6], humbuf2[6];
107193323Sed    char cwd[FILENAME_MAX];
108193323Sed
109193323Sed    if (!find_play_pen(pen, sz))
110193323Sed	return NULL;
111193323Sed
112193323Sed    if (!mkdtemp(pen)) {
113193323Sed	cleanup(0);
114193323Sed	errx(2, "%s: can't mktemp '%s'", __func__, pen);
115193323Sed    }
116193323Sed    if (chmod(pen, 0700) == FAIL) {
117193323Sed	cleanup(0);
118193323Sed	errx(2, "%s: can't mkdir '%s'", __func__, pen);
119193323Sed    }
120193323Sed
121193323Sed    if (Verbose) {
122193323Sed	if (sz) {
123193323Sed	    humanize_number(humbuf1, sizeof humbuf1, sz, "", HN_AUTOSCALE,
124193323Sed	        HN_NOSPACE);
125193323Sed	    humanize_number(humbuf2, sizeof humbuf2, min_free(pen),
126193323Sed	        "", HN_AUTOSCALE, HN_NOSPACE);
127193323Sed	    fprintf(stderr, "Requested space: %s bytes, free space: %s bytes in %s\n", humbuf1, humbuf2, pen);
128193323Sed	}
129193323Sed    }
130193323Sed
131193323Sed    if (min_free(pen) < sz) {
132193323Sed	rmdir(pen);
133193323Sed	cleanup(0);
134193323Sed	errx(2, "%s: not enough free space to create '%s'.\n"
135193323Sed	     "Please set your PKG_TMPDIR environment variable to a location\n"
136193323Sed	     "with more space and\ntry the command again", __func__, pen);
137193323Sed    }
138193323Sed
139193323Sed    if (!getcwd(cwd, FILENAME_MAX)) {
140193323Sed	upchuck("getcwd");
141193323Sed	return NULL;
142193323Sed    }
143193323Sed
144193323Sed    if (chdir(pen) == FAIL) {
145193323Sed	cleanup(0);
146193323Sed	errx(2, "%s: can't chdir to '%s'", __func__, pen);
147193323Sed    }
148193323Sed
149193323Sed    strcpy(PenLocation, pen);
150193323Sed    return pushPen(cwd);
151193323Sed}
152193323Sed
153193323Sed/* Convenience routine for getting out of playpen */
154193323Sedint
155193323Sedleave_playpen()
156193323Sed{
157193323Sed    static char left[FILENAME_MAX];
158193323Sed    void (*oldsig)(int);
159193323Sed
160193323Sed    if (!PenLocation[0])
161193323Sed	return 0;
162193323Sed
163193323Sed    /* Don't interrupt while we're cleaning up */
164193323Sed    oldsig = signal(SIGINT, SIG_IGN);
165193323Sed    strcpy(left, PenLocation);
166193323Sed    popPen(PenLocation);
167193323Sed
168193323Sed    if (chdir(PenLocation) == FAIL) {
169193323Sed	cleanup(0);
170193323Sed	errx(2, "%s: can't chdir back to '%s'", __func__, PenLocation);
171193323Sed    }
172193323Sed
173193323Sed    if (left[0] == '/' && vsystem("/bin/rm -rf %s", left))
174193323Sed	warnx("couldn't remove temporary dir '%s'", left);
175193323Sed    signal(SIGINT, oldsig);
176193323Sed
177193323Sed    return 1;
178193323Sed}
179193323Sed
180193323Sedoff_t
181193323Sedmin_free(const char *tmpdir)
182193323Sed{
183198090Srdivacky    struct statfs buf;
184193323Sed
185198090Srdivacky    if (statfs(tmpdir, &buf) != 0) {
186193323Sed	warn("statfs");
187193323Sed	return -1;
188193323Sed    }
189193323Sed    return (off_t)buf.f_bavail * (off_t)buf.f_bsize;
190193323Sed}
191193323Sed