8
8
*
9
9
*
10
10
* IDENTIFICATION
11
- * $Header: /cvsroot/pgsql/contrib/vacuumlo/vacuumlo.c,v 1.10 2001/09/17 02:30:54 inoue Exp $
11
+ * $Header: /cvsroot/pgsql/contrib/vacuumlo/vacuumlo.c,v 1.11 2002/04/24 02:45:51 momjian Exp $
12
12
*
13
13
*-------------------------------------------------------------------------
14
14
*/
15
+
16
+ #include <pg_config.h>
15
17
#include <stdio.h>
16
18
#include <stdlib.h>
17
19
#include <string.h>
18
20
21
+ #ifdef HAVE_TERMIOS_H
22
+ #include <termios.h>
23
+ #endif
24
+
19
25
#include <sys/types.h>
20
26
#include <sys/stat.h>
21
27
#include <fcntl.h>
28
34
29
35
#define BUFSIZE 1024
30
36
31
- int vacuumlo (char * ,int );
37
+ extern char * optarg ;
38
+ extern int optind ,opterr ,optopt ;
39
+
40
+ struct _param {
41
+ char * pg_user ;
42
+ int pg_prompt ;
43
+ char * pg_port ;
44
+ char * pg_host ;
45
+ int verbose ;
46
+ int dry_run ;
47
+ };
48
+
49
+ int vacuumlo (char * ,struct _param * );
50
+ char * simple_prompt (const char * prompt ,int ,int );
51
+ void usage (void );
52
+
53
+
54
+ /*
55
+ * simple_prompt
56
+ *
57
+ * Generalized function especially intended for reading in usernames and
58
+ * password interactively. Reads from /dev/tty or stdin/stderr.
59
+ *
60
+ * prompt:The prompt to print
61
+ * maxlen:How many characters to accept
62
+ * echo:Set to 0 if you want to hide what is entered (for passwords)
63
+ *
64
+ * Returns a malloc()'ed string with the input (w/o trailing newline).
65
+ */
66
+ static int prompt_state = 0 ;
67
+
68
+ char *
69
+ simple_prompt (const char * prompt ,int maxlen ,int echo )
70
+ {
71
+ int length ;
72
+ char * destination ;
73
+ FILE * termin ,
74
+ * termout ;
75
+
76
+ #ifdef HAVE_TERMIOS_H
77
+ struct termios t_orig ,
78
+ t ;
79
+ #endif
80
+
81
+ destination = (char * )malloc (maxlen + 2 );
82
+ if (!destination )
83
+ return NULL ;
84
+
85
+ prompt_state = 1 ;/* disable SIGINT */
86
+
87
+ /*
88
+ * Do not try to collapse these into one "w+" mode file. Doesn't work
89
+ * on some platforms (eg, HPUX 10.20).
90
+ */
91
+ termin = fopen ("/dev/tty" ,"r" );
92
+ termout = fopen ("/dev/tty" ,"w" );
93
+ if (!termin || !termout )
94
+ {
95
+ if (termin )
96
+ fclose (termin );
97
+ if (termout )
98
+ fclose (termout );
99
+ termin = stdin ;
100
+ termout = stderr ;
101
+ }
102
+
103
+ #ifdef HAVE_TERMIOS_H
104
+ if (!echo )
105
+ {
106
+ tcgetattr (fileno (termin ),& t );
107
+ t_orig = t ;
108
+ t .c_lflag &= ~ECHO ;
109
+ tcsetattr (fileno (termin ),TCSAFLUSH ,& t );
110
+ }
111
+ #endif
112
+
113
+ if (prompt )
114
+ {
115
+ fputs (prompt ,termout );
116
+ fflush (termout );
117
+ }
118
+
119
+ if (fgets (destination ,maxlen ,termin )== NULL )
120
+ destination [0 ]= '\0' ;
121
+
122
+ length = strlen (destination );
123
+ if (length > 0 && destination [length - 1 ]!= '\n' )
124
+ {
125
+ /* eat rest of the line */
126
+ char buf [128 ];
127
+ int buflen ;
128
+
129
+ do
130
+ {
131
+ if (fgets (buf ,sizeof (buf ),termin )== NULL )
132
+ break ;
133
+ buflen = strlen (buf );
134
+ }while (buflen > 0 && buf [buflen - 1 ]!= '\n' );
135
+ }
136
+
137
+ if (length > 0 && destination [length - 1 ]== '\n' )
138
+ /* remove trailing newline */
139
+ destination [length - 1 ]= '\0' ;
140
+
141
+ #ifdef HAVE_TERMIOS_H
142
+ if (!echo )
143
+ {
144
+ tcsetattr (fileno (termin ),TCSAFLUSH ,& t_orig );
145
+ fputs ("\n" ,termout );
146
+ fflush (termout );
147
+ }
148
+ #endif
149
+
150
+ if (termin != stdin )
151
+ {
152
+ fclose (termin );
153
+ fclose (termout );
154
+ }
155
+
156
+ prompt_state = 0 ;/* SIGINT okay again */
157
+
158
+ return destination ;
159
+ }
160
+
32
161
33
162
34
163
/*
35
164
* This vacuums LOs of one database. It returns 0 on success, -1 on failure.
36
165
*/
37
166
int
38
- vacuumlo (char * database ,int verbose )
167
+ vacuumlo (char * database ,struct _param * param )
39
168
{
40
169
PGconn * conn ;
41
170
PGresult * res ,
42
- * res2 ;
171
+ * res2 ;
43
172
char buf [BUFSIZE ];
44
- int matched ;
45
- int deleted ;
46
- int i ;
173
+ int matched ;
174
+ int deleted ;
175
+ int i ;
176
+ char * password = NULL ;
47
177
48
- conn = PQsetdb (NULL ,NULL ,NULL ,NULL ,database );
178
+ if (param -> pg_prompt ) {
179
+ password = simple_prompt ("Password: " ,32 ,0 );
180
+ if (!password ) {
181
+ fprintf (stderr ,"failed to get password\n" );
182
+ exit (1 );
183
+ }
184
+ }
185
+
186
+ conn = PQsetdbLogin (param -> pg_host ,
187
+ param -> pg_port ,
188
+ NULL ,
189
+ NULL ,
190
+ database ,
191
+ param -> pg_user ,
192
+ password
193
+ );
49
194
50
195
/* check to see that the backend connection was successfully made */
51
196
if (PQstatus (conn )== CONNECTION_BAD )
@@ -56,8 +201,11 @@ vacuumlo(char *database, int verbose)
56
201
return -1 ;
57
202
}
58
203
59
- if (verbose )
204
+ if (param -> verbose ) {
60
205
fprintf (stdout ,"Connected to %s\n" ,database );
206
+ if (param -> dry_run )
207
+ fprintf (stdout ,"Test run: no large objects will be removed!\n" );
208
+ }
61
209
62
210
/*
63
211
* First we create and populate the LO temp table
@@ -132,7 +280,7 @@ vacuumlo(char *database, int verbose)
132
280
table = PQgetvalue (res ,i ,0 );
133
281
field = PQgetvalue (res ,i ,1 );
134
282
135
- if (verbose )
283
+ if (param -> verbose )
136
284
fprintf (stdout ,"Checking %s in %s\n" ,field ,table );
137
285
138
286
/*
@@ -188,19 +336,22 @@ vacuumlo(char *database, int verbose)
188
336
{
189
337
Oid lo = atooid (PQgetvalue (res ,i ,0 ));
190
338
191
- if (verbose )
339
+ if (param -> verbose )
192
340
{
193
341
fprintf (stdout ,"\rRemoving lo %6u " ,lo );
194
342
fflush (stdout );
195
343
}
196
344
197
- if (lo_unlink (conn ,lo )< 0 )
198
- {
199
- fprintf (stderr ,"\nFailed to remove lo %u: " ,lo );
200
- fprintf (stderr ,"%s" ,PQerrorMessage (conn ));
201
- }
202
- else
203
- deleted ++ ;
345
+ if (param -> dry_run == 0 ) {
346
+ if (lo_unlink (conn ,lo )< 0 )
347
+ {
348
+ fprintf (stderr ,"\nFailed to remove lo %u: " ,lo );
349
+ fprintf (stderr ,"%s" ,PQerrorMessage (conn ));
350
+ }
351
+ else
352
+ deleted ++ ;
353
+ }else
354
+ deleted ++ ;
204
355
}
205
356
PQclear (res );
206
357
@@ -212,33 +363,95 @@ vacuumlo(char *database, int verbose)
212
363
213
364
PQfinish (conn );
214
365
215
- if (verbose )
216
- fprintf (stdout ,"\rRemoved %d large objects from %s.\n" ,
217
- deleted ,database );
366
+ if (param -> verbose )
367
+ fprintf (stdout ,"\r%s %d large objects from %s.\n" ,
368
+ ( param -> dry_run ? "Would remove" : "Removed" ), deleted ,database );
218
369
219
370
return 0 ;
220
371
}
221
372
373
+ void
374
+ usage (void ) {
375
+ fprintf (stdout ,"vacuumlo removes unreferenced large objects from databases\n\n" );
376
+ fprintf (stdout ,"Usage:\n vacuumlo [options] dbname [dbnames...]\n\n" );
377
+ fprintf (stdout ,"Options:\n" );
378
+ fprintf (stdout ," -v\t\tWrite a lot of output\n" );
379
+ fprintf (stdout ," -n\t\tDon't remove any large object, just show what would be done\n" );
380
+ fprintf (stdout ," -U username\tUsername to connect as\n" );
381
+ fprintf (stdout ," -W\t\tPrompt for password\n" );
382
+ fprintf (stdout ," -h hostname\tDatabase server host\n" );
383
+ fprintf (stdout ," -p port\tDatabase server port\n" );
384
+ fprintf (stdout ," -p port\tDatabase server port\n\n" );
385
+ }
386
+
387
+
222
388
int
223
389
main (int argc ,char * * argv )
224
390
{
225
- int verbose = 0 ;
226
- int arg ;
227
391
int rc = 0 ;
392
+ struct _param param ;
393
+ int c ;
394
+ int port ;
228
395
229
- if (argc < 2 )
230
- {
231
- fprintf (stderr ,"Usage: %s [-v] database_name [db2 ... dbn]\n" ,
232
- argv [0 ]);
396
+ /* Parameter handling */
397
+ param .pg_user = NULL ;
398
+ param .pg_prompt = 0 ;
399
+ param .pg_host = NULL ;
400
+ param .pg_port = 0 ;
401
+ param .verbose = 0 ;
402
+ param .dry_run = 0 ;
403
+
404
+ while (1 ) {
405
+ c = getopt (argc ,argv ,"?h:U:p:vnW" );
406
+ if (c == -1 )
407
+ break ;
408
+
409
+ switch (c ) {
410
+ case '?' :
411
+ if (optopt == '?' ) {
412
+ usage ();
413
+ exit (0 );
414
+ }
415
+ exit (1 );
416
+ case ':' :
417
+ exit (1 );
418
+ case 'v' :
419
+ param .verbose = 1 ;
420
+ break ;
421
+ case 'n' :
422
+ param .dry_run = 1 ;
423
+ param .verbose = 1 ;
424
+ break ;
425
+ case 'U' :
426
+ param .pg_user = strdup (optarg );
427
+ break ;
428
+ case 'W' :
429
+ param .pg_prompt = 1 ;
430
+ break ;
431
+ case 'p' :
432
+ port = strtol (optarg ,NULL ,10 );
433
+ if ( (port < 1 )|| (port > 65535 )) {
434
+ fprintf (stderr ,"[%s]: invalid port number '%s'\n" ,argv [0 ],optarg );
233
435
exit (1 );
436
+ }
437
+ param .pg_port = strdup (optarg );
438
+ break ;
439
+ case 'h' :
440
+ param .pg_host = strdup (optarg );
441
+ break ;
442
+ }
443
+ }
444
+
445
+ /* No database given? Show usage */
446
+ if (optind >=argc - 1 ) {
447
+ fprintf (stderr ,"vacuumlo: missing required argument: database name\n" );
448
+ fprintf (stderr ,"Try 'vacuumlo -?' for help.\n" );
449
+ exit (1 );
234
450
}
235
451
236
- for (arg = 1 ;arg < argc ;arg ++ )
237
- {
238
- if (strcmp ("-v" ,argv [arg ])== 0 )
239
- verbose = !verbose ;
240
- else
241
- rc += (vacuumlo (argv [arg ],verbose )!= 0 );
452
+ for (c = optind ;c < argc ;c ++ ) {
453
+ /* Work on selected database */
454
+ rc += (vacuumlo (argv [c ],& param )!= 0 );
242
455
}
243
456
244
457
return rc ;