2 * sponge.c - read in all available info from stdin, then output it to
3 * file named on the command line
5 * Copyright © 2006 Tollef Fog Heen
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * version 2 as published by the Free Software Foundation.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
26 #include <sys/types.h>
29 #include <sys/param.h>
33 #include <sys/resource.h>
40 #define MIN_SPONGE_SIZE sizeof(8192)
41 #define BUFF_SIZE 8192
42 #define DEFAULT_TMP_NAME "/tmp/sponge.XXXXXX"
43 char tmpname
[] = DEFAULT_TMP_NAME
;
46 printf("sponge <file>: suck in all input from stdin and write it to <file>\n");
50 /* all the signal stuff copied from gnu sort */
52 /* The set of signals that are caught. */
53 static sigset_t caught_signals
;
55 /* Critical section status. */
57 int valid
; // was bool
61 /* Enter a critical section. */
62 static struct cs_status
cs_enter (void) {
63 struct cs_status status
;
64 status
.valid
= (sigprocmask(SIG_BLOCK
, &caught_signals
, &status
.sigs
) == 0);
68 /* Leave a critical section. */
69 static void cs_leave (struct cs_status status
) {
71 /* Ignore failure when restoring the signal mask. */
72 sigprocmask(SIG_SETMASK
, &status
.sigs
, NULL
);
76 static void cleanup() {
80 static void onexit_cleanup (void) {
81 struct cs_status cs
= cs_enter();
86 static void sighandler (int sig
) {
96 /* taken from coreutils sort */
97 static size_t default_sponge_size (void) {
98 /* Let MEM be available memory or 1/8 of total memory, whichever
100 double avail
= physmem_available();
101 double total
= physmem_total();
102 double mem
= MAX(avail
, total
/ 8);
103 struct rlimit rlimit
;
105 /* Let SIZE be MEM, but no more than the maximum object size or
106 system resource limits. Avoid the MIN macro here, as it is not
107 quite right when only one argument is floating point. Don't
108 bother to check for values like RLIM_INFINITY since in practice
109 they are not much less than SIZE_MAX. */
110 size_t size
= SIZE_MAX
;
113 if (getrlimit(RLIMIT_DATA
, &rlimit
) == 0 && rlimit
.rlim_cur
< size
)
114 size
= rlimit
.rlim_cur
;
116 if (getrlimit(RLIMIT_AS
, &rlimit
) == 0 && rlimit
.rlim_cur
< size
)
117 size
= rlimit
.rlim_cur
;
120 /* Leave a large safety margin for the above limits, as failure can
121 occur when they are exceeded. */
125 /* Leave a 1/16 margin for RSS to leave room for code, stack, etc.
126 Exceeding RSS is not fatal, but can be quite slow. */
127 if (getrlimit(RLIMIT_RSS
, &rlimit
) == 0 && rlimit
.rlim_cur
/ 16 * 15 < size
)
128 size
= rlimit
.rlim_cur
/ 16 * 15;
131 /* Use no less than the minimum. */
132 return MAX (size
, MIN_SPONGE_SIZE
);
135 void trapsignals (void) {
137 static int const sig
[] = {
138 /* The usual suspects. */
139 SIGALRM
, SIGHUP
, SIGINT
, SIGPIPE
, SIGQUIT
, SIGTERM
,
156 int nsigs
= sizeof(sig
) / sizeof(sig
[0]);
159 struct sigaction act
;
161 sigemptyset(&caught_signals
);
162 for (i
= 0; i
< nsigs
; i
++) {
163 sigaction(sig
[i
], NULL
, &act
);
164 if (act
.sa_handler
!= SIG_IGN
)
165 sigaddset(&caught_signals
, sig
[i
]);
168 act
.sa_handler
= sighandler
;
169 act
.sa_mask
= caught_signals
;
172 for (i
= 0; i
< nsigs
; i
++)
173 if (sigismember(&caught_signals
, sig
[i
]))
174 sigaction(sig
[i
], &act
, NULL
);
176 for (i
= 0; i
< nsigs
; i
++)
177 if (signal(sig
[i
], SIG_IGN
) != SIG_IGN
) {
178 signal(sig
[i
], sighandler
);
179 siginterrupt (sig
[i
], 1);
184 int main (int argc
, char **argv
) {
185 char *buf
, *bufstart
, *outname
= NULL
;
186 size_t bufsize
= BUFF_SIZE
;
190 size_t mem_available
= default_sponge_size();
192 if (argc
> 2 || (argc
== 2 && strcmp(argv
[1], "-h") == 0)) {
195 bufstart
= buf
= malloc(bufsize
);
197 perror("failed to allocate memory");
203 while ((i
= read(0, buf
, bufsize
- bufused
)) > 0) {
205 if (bufused
== bufsize
) {
206 if (bufsize
>= mem_available
) {
209 struct cs_status cs
= cs_enter();
210 int tmpfd
= mkstemp(tmpname
);
211 atexit(onexit_cleanup
); // if solaris on_exit(onexit_cleanup, 0);
217 tmpfile
= fdopen(tmpfd
, "w+");
219 if (fwrite(bufstart
, bufsize
, 1, tmpfile
) < 1) {
220 perror("writing to tempory file failed");
228 bufstart
= realloc(bufstart
, bufsize
);
230 perror("failed to realloc memory");
235 buf
= bufstart
+ bufused
;
238 perror("failed to read from stdin");
245 if (fwrite(bufstart
, bufused
, 1, tmpfile
) < 1) {
246 perror("write tmpfile");
252 if (rename(tmpname
, outname
)) {
253 perror("error renaming temporary file to output file");
258 if (fseek(tmpfile
, 0, SEEK_SET
)) {
259 perror("could to seek to start of temporary file");
263 while (fread( buf
, BUFF_SIZE
, 1, tmpfile
) < 1) {
264 if (fwrite(buf
, BUFF_SIZE
, 1, stdout
) < 1) {
265 perror("error writing out merged file");
275 FILE *outfd
= fopen(outname
, "w");
277 perror("error opening output file");
280 if (fwrite(bufstart
, bufused
, 1, outfd
) < 1) {
281 perror("error writing out merged file");
285 else if (fwrite(bufstart
, bufused
, 1, stdout
) < 1) {
286 perror("error writing out merged file");