88 *
99 *
1010 * 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 $
1212 *
1313 *-------------------------------------------------------------------------
1414 */
15+
16+ #include <pg_config.h>
1517#include <stdio.h>
1618#include <stdlib.h>
1719#include <string.h>
1820
21+ #ifdef HAVE_TERMIOS_H
22+ #include <termios.h>
23+ #endif
24+
1925#include <sys/types.h>
2026#include <sys/stat.h>
2127#include <fcntl.h>
2834
2935#define BUFSIZE 1024
3036
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+
32161
33162
34163/*
35164 * This vacuums LOs of one database. It returns 0 on success, -1 on failure.
36165 */
37166int
38- vacuumlo (char * database ,int verbose )
167+ vacuumlo (char * database ,struct _param * param )
39168{
40169PGconn * conn ;
41170PGresult * res ,
42- * res2 ;
171+ * res2 ;
43172char buf [BUFSIZE ];
44- int matched ;
45- int deleted ;
46- int i ;
173+ int matched ;
174+ int deleted ;
175+ int i ;
176+ char * password = NULL ;
47177
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+ );
49194
50195/* check to see that the backend connection was successfully made */
51196if (PQstatus (conn )== CONNECTION_BAD )
@@ -56,8 +201,11 @@ vacuumlo(char *database, int verbose)
56201return -1 ;
57202}
58203
59- if (verbose )
204+ if (param -> verbose ) {
60205fprintf (stdout ,"Connected to %s\n" ,database );
206+ if (param -> dry_run )
207+ fprintf (stdout ,"Test run: no large objects will be removed!\n" );
208+ }
61209
62210/*
63211 * First we create and populate the LO temp table
@@ -132,7 +280,7 @@ vacuumlo(char *database, int verbose)
132280table = PQgetvalue (res ,i ,0 );
133281field = PQgetvalue (res ,i ,1 );
134282
135- if (verbose )
283+ if (param -> verbose )
136284fprintf (stdout ,"Checking %s in %s\n" ,field ,table );
137285
138286/*
@@ -188,19 +336,22 @@ vacuumlo(char *database, int verbose)
188336{
189337Oid lo = atooid (PQgetvalue (res ,i ,0 ));
190338
191- if (verbose )
339+ if (param -> verbose )
192340{
193341fprintf (stdout ,"\rRemoving lo %6u " ,lo );
194342fflush (stdout );
195343}
196344
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 ++ ;
204355}
205356PQclear (res );
206357
@@ -212,33 +363,95 @@ vacuumlo(char *database, int verbose)
212363
213364PQfinish (conn );
214365
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 );
218369
219370return 0 ;
220371}
221372
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+
222388int
223389main (int argc ,char * * argv )
224390{
225- int verbose = 0 ;
226- int arg ;
227391int rc = 0 ;
392+ struct _param param ;
393+ int c ;
394+ int port ;
228395
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 );
233435exit (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 );
234450}
235451
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 );
242455}
243456
244457return rc ;