diff -c pg_autovacuum.orig/pg_autovacuum.c pg_autovacuum/pg_autovacuum.c *** pg_autovacuum.orig/pg_autovacuum.c Fri Apr 2 00:35:35 2004 --- pg_autovacuum/pg_autovacuum.c Thu May 6 14:15:59 2004 *************** *** 1,17 **** /* pg_autovacuum.c * All the code for the pg_autovacuum program * (c) 2003 Matthew T. O'Connor ! * Revisions by Christopher B. Browne, Liberty RMS */ ! #include "pg_autovacuum.h" FILE *LOGOUTPUT; char logbuffer[4096]; static void log_entry(const char *logentry) ! { time_t curtime; struct tm *loctime; char timebuffer[128]; --- 1,29 ---- /* pg_autovacuum.c * All the code for the pg_autovacuum program * (c) 2003 Matthew T. O'Connor ! * Revisions by Christopher B. Browne, Liberty RMS ! * Win32 Service code added by Dave Page */ ! #include "pg_autovacuum.h" + + #ifdef WIN32 + #include + + unsigned int sleep(); + + SERVICE_STATUS ServiceStatus; + SERVICE_STATUS_HANDLE hStatus; + int appMode = 0; + #endif FILE *LOGOUTPUT; char logbuffer[4096]; static void log_entry(const char *logentry) ! { ! #ifndef WIN32 time_t curtime; struct tm *loctime; char timebuffer[128]; *************** *** 19,25 **** curtime = time(NULL); loctime = localtime(&curtime); strftime(timebuffer, sizeof(timebuffer), "%Y-%m-%d %H:%M:%S %Z", loctime); ! fprintf(LOGOUTPUT, "[%s] %s\n", timebuffer, logentry); } /* --- 31,58 ---- curtime = time(NULL); loctime = localtime(&curtime); strftime(timebuffer, sizeof(timebuffer), "%Y-%m-%d %H:%M:%S %Z", loctime); ! fprintf(LOGOUTPUT, "[%s] %s\n", timebuffer, logentry); ! #else ! static HANDLE evtHandle = INVALID_HANDLE_VALUE; ! ! if (evtHandle == INVALID_HANDLE_VALUE) { ! evtHandle = RegisterEventSource(NULL,"PostgreSQL Auto Vacuum Daemon"); ! if (evtHandle == NULL) { ! evtHandle = INVALID_HANDLE_VALUE; ! return; ! } ! } ! ! ReportEvent(evtHandle, ! EVENTLOG_SUCCESS, /* FIXME - Proper error levels?? */ ! 0, ! 0, /* FIXME - Proper IDs (to get decent messages) */ ! NULL, ! 1, ! 0, ! &logentry, ! NULL); ! #endif } /* *************** *** 28,34 **** * * This code is mostly ripped directly from pm_dameonize in postmaster.c with * unneeded code removed. ! */ static void daemonize() { --- 61,68 ---- * * This code is mostly ripped directly from pm_dameonize in postmaster.c with * unneeded code removed. ! */ ! #ifndef WIN32 static void daemonize() { *************** *** 58,64 **** } #endif ! } /* Create and return tbl_info struct with initialized to values from row or res */ static tbl_info * --- 92,99 ---- } #endif ! } ! #endif /* WIN32 */ /* Create and return tbl_info struct with initialized to values from row or res */ static tbl_info * *************** *** 824,831 **** args->vacuum_scaling_factor = VACSCALINGFACTOR; args->analyze_base_threshold = -1; args->analyze_scaling_factor = -1; ! args->debug = AUTOVACUUM_DEBUG; ! args->daemonize = 0; args->user = 0; args->password = 0; args->host = 0; --- 859,871 ---- args->vacuum_scaling_factor = VACSCALINGFACTOR; args->analyze_base_threshold = -1; args->analyze_scaling_factor = -1; ! args->debug = AUTOVACUUM_DEBUG; ! #ifndef WIN32 ! args->daemonize = 0; ! #else ! args->install_as_service = 0; ! args->remove_as_service = 0; ! #endif args->user = 0; args->password = 0; args->host = 0; *************** *** 835,842 **** /* * Fixme: Should add some sanity checking such as positive integer * values etc ! */ ! while ((c = getopt(argc, argv, "s:S:v:V:a:A:d:U:P:H:L:p:hD")) != -1) { switch (c) { --- 875,886 ---- /* * Fixme: Should add some sanity checking such as positive integer * values etc ! */ ! #ifndef WIN32 ! while ((c = getopt(argc, argv, "s:S:v:V:a:A:d:U:P:H:L:p:hD")) != -1) ! #else ! while ((c = getopt(argc, argv, "s:S:v:V:a:A:d:U:P:H:L:p:hIR")) != -1) ! #endif { switch (c) { *************** *** 857,866 **** break; case 'A': args->analyze_scaling_factor = atof(optarg); ! break; case 'D': args->daemonize++; ! break; case 'd': args->debug = atoi(optarg); break; --- 901,912 ---- break; case 'A': args->analyze_scaling_factor = atof(optarg); ! break; ! #ifndef WIN32 case 'D': args->daemonize++; ! break; ! #endif case 'd': args->debug = atoi(optarg); break; *************** *** 881,887 **** break; case 'h': usage(); ! exit(0); default: /* --- 927,941 ---- break; case 'h': usage(); ! exit(0); ! #ifdef WIN32 ! case 'I': ! args->install_as_service++; ! break; ! case 'R': ! args->remove_as_service++; ! break; ! #endif default: /* *************** *** 912,919 **** int i = 0; float f = 0; ! fprintf(stderr, "usage: pg_autovacuum \n"); ! fprintf(stderr, " [-D] Daemonize (Detach from tty and run in the background)\n"); i = AUTOVACUUM_DEBUG; fprintf(stderr, " [-d] debug (debug level=0,1,2,3; default=%i)\n", i); --- 966,978 ---- int i = 0; float f = 0; ! fprintf(stderr, "usage: pg_autovacuum \n"); ! #ifndef WIN32 ! fprintf(stderr, " [-D] Daemonize (Detach from tty and run in the background)\n"); ! #else ! fprintf(stderr, " [-I] Install as a Windows service (all other options will be ignored)\n"); ! fprintf(stderr, " [-R] Remove as a Windows service (all other options will be ignored)\n"); ! #endif i = AUTOVACUUM_DEBUG; fprintf(stderr, " [-d] debug (debug level=0,1,2,3; default=%i)\n", i); *************** *** 955,964 **** sprintf(logbuffer, " args->password=%s", (args->password) ? args->password : "(null)"); log_entry(logbuffer); sprintf(logbuffer, " args->logfile=%s", (args->logfile) ? args->logfile : "(null)"); ! log_entry(logbuffer); sprintf(logbuffer, " args->daemonize=%i", args->daemonize); ! log_entry(logbuffer); ! sprintf(logbuffer, " args->sleep_base_value=%i", args->sleep_base_value); log_entry(logbuffer); sprintf(logbuffer, " args->sleep_scaling_factor=%f", args->sleep_scaling_factor); --- 1014,1030 ---- sprintf(logbuffer, " args->password=%s", (args->password) ? args->password : "(null)"); log_entry(logbuffer); sprintf(logbuffer, " args->logfile=%s", (args->logfile) ? args->logfile : "(null)"); ! log_entry(logbuffer); ! #ifndef WIN32 sprintf(logbuffer, " args->daemonize=%i", args->daemonize); ! log_entry(logbuffer); ! #else ! sprintf(logbuffer, " args->install_as_service=%i", args->install_as_service); ! log_entry(logbuffer); ! sprintf(logbuffer, " args->remove_as_service=%i", args->remove_as_service); ! log_entry(logbuffer); ! #endif ! sprintf(logbuffer, " args->sleep_base_value=%i", args->sleep_base_value); log_entry(logbuffer); sprintf(logbuffer, " args->sleep_scaling_factor=%f", args->sleep_scaling_factor); *************** *** 976,1023 **** fflush(LOGOUTPUT); } ! ! /* Beginning of AutoVacuum Main Program */ ! int ! main(int argc, char *argv[]) ! { char buf[256]; ! int j = 0, ! loops = 0; /* int numInserts, numDeletes, */ ! int sleep_secs; ! Dllist *db_list; ! Dlelem *db_elem, ! *tbl_elem; ! db_info *dbs; ! tbl_info *tbl; ! PGresult *res = NULL; ! double diff; ! struct timeval now, ! then; ! ! args = get_cmd_args(argc, argv); /* Get Command Line Args and put ! * them in the args struct */ ! ! /* Dameonize if requested */ ! if (args->daemonize == 1) ! daemonize(); ! ! if (args->logfile) ! { ! LOGOUTPUT = fopen(args->logfile, "a"); ! if (!LOGOUTPUT) ! { ! fprintf(stderr, "Could not open log file - [%s]\n", args->logfile); ! exit(-1); ! } ! } ! else ! LOGOUTPUT = stderr; ! if (args->debug >= 2) ! print_cmd_args(); ! /* Init the db list with template1 */ db_list = init_db_list(); if (db_list == NULL) --- 1042,1193 ---- fflush(LOGOUTPUT); } ! ! #ifdef WIN32 ! ! /* Handle control requests from the Service Control Manager */ ! static void ! ControlHandler(DWORD request) ! { ! switch(request) ! { ! case SERVICE_CONTROL_STOP: ! log_entry("INFO: Service stopped"); ! fflush(LOGOUTPUT); ! ServiceStatus.dwWin32ExitCode = 0; ! ServiceStatus.dwCurrentState = SERVICE_STOPPED; ! SetServiceStatus (hStatus, &ServiceStatus); ! return; ! ! case SERVICE_CONTROL_SHUTDOWN: ! log_entry("INFO: Service stopped"); ! fflush(LOGOUTPUT); ! ServiceStatus.dwWin32ExitCode = 0; ! ServiceStatus.dwCurrentState = SERVICE_STOPPED; ! SetServiceStatus (hStatus, &ServiceStatus); ! return; ! ! default: ! break; ! } ! ! // Report current status ! SetServiceStatus (hStatus, &ServiceStatus); ! ! return; ! } ! ! /* Register with the Service Control Manager */ ! static int ! InstallService() ! { ! SC_HANDLE schService = NULL; ! SC_HANDLE schSCManager = NULL; ! char szFilename[MAX_PATH]; ! ! // Get the module name ! GetModuleFileName(NULL, szFilename, MAX_PATH); ! ! // Open the Service Control Manager on the local computer. ! schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); ! ! // Check connection to the SCM ! if (! schSCManager) return -1; ! ! schService = CreateService( ! schSCManager, // SCManager database ! TEXT("pg_autovacuum"), // Name of service ! TEXT("PostgreSQL Auto Vacuum Daemon"), // Name to display ! SERVICE_ALL_ACCESS, // Desired access ! SERVICE_WIN32_OWN_PROCESS, // Service type ! SERVICE_AUTO_START, // Start type ! SERVICE_ERROR_NORMAL, // Error control type ! szFilename, // Service binary ! NULL, // No load ordering group ! NULL, // No tag identifier ! NULL, // Dependencies ! NULL, // Service account ! NULL); // Account password ! ! if (! schService) return -2; ! ! return 0; ! } ! ! /* Unregister from the Service Control Manager */ ! static int ! RemoveService() ! { ! SC_HANDLE schService = NULL; ! SC_HANDLE schSCManager = NULL; ! ! // Open the Service Control Manager on the local computer. ! schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); ! ! // Check connection to the SCM ! if (!schSCManager) return -1; ! ! // Open the service ! schService = OpenService(schSCManager, TEXT("pg_autovacuum"), SC_MANAGER_ALL_ACCESS); ! ! // Check connection to the service ! if (!schService) return -2; ! ! // Delete the service ! if (!DeleteService(schService)) return -3; ! ! return 0; ! } ! ! #endif /* WIN32 */ ! ! static ! int VacuumLoop(int argc, char** argv) ! { char buf[256]; ! int j = 0, ! loops = 0; /* int numInserts, numDeletes, */ ! int sleep_secs; ! Dllist *db_list; ! Dlelem *db_elem, ! *tbl_elem; ! db_info *dbs; ! tbl_info *tbl; ! PGresult *res = NULL; ! double diff; ! ! struct timeval now, ! then; ! ! ! log_entry("Going..."); ! #ifdef WIN32 ! ! ServiceStatus.dwServiceType = SERVICE_WIN32; ! ServiceStatus.dwCurrentState = SERVICE_START_PENDING; ! ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN; ! ServiceStatus.dwWin32ExitCode = 0; ! ServiceStatus.dwServiceSpecificExitCode = 0; ! ServiceStatus.dwCheckPoint = 0; ! ServiceStatus.dwWaitHint = 0; ! ! if (!appMode) { ! hStatus = RegisterServiceCtrlHandler("pg_autovacuum", (LPHANDLER_FUNCTION)ControlHandler); ! if (hStatus == (SERVICE_STATUS_HANDLE)0) ! { ! // Registering Control Handler failed ! return -1; ! } ! } ! ! // We report the running status to SCM. ! ServiceStatus.dwCurrentState = SERVICE_RUNNING; ! if (!appMode) SetServiceStatus (hStatus, &ServiceStatus); ! ! #endif /* WIN32 */ ! /* Init the db list with template1 */ db_list = init_db_list(); if (db_list == NULL) *************** *** 1030,1041 **** fflush(LOGOUTPUT); exit(1); ! } ! gettimeofday(&then, 0); /* for use later to caluculate sleep time */ ! ! while (1) ! { /* Main Loop */ db_elem = DLGetHead(db_list); /* Reset cur_db_node to the * beginning of the db_list */ --- 1200,1217 ---- fflush(LOGOUTPUT); exit(1); ! } ! gettimeofday(&then, 0); /* for use later to caluculate sleep time */ ! ! #ifndef WIN32 ! while (1) ! #else ! while (ServiceStatus.dwCurrentState == SERVICE_RUNNING) ! #endif ! { ! /* Main Loop */ ! db_elem = DLGetHead(db_list); /* Reset cur_db_node to the * beginning of the db_list */ *************** *** 1049,1055 **** * template1 */ log_entry("Error: Cannot connect to template1, exiting."); fflush(LOGOUTPUT); ! fclose(LOGOUTPUT); exit(1); } } --- 1225,1236 ---- * template1 */ log_entry("Error: Cannot connect to template1, exiting."); fflush(LOGOUTPUT); ! fclose(LOGOUTPUT); ! #ifdef WIN32 ! ServiceStatus.dwCurrentState = SERVICE_STOPPED; ! ServiceStatus.dwWin32ExitCode = -1; ! if(!appMode) SetServiceStatus(hStatus, &ServiceStatus); ! #endif exit(1); } } *************** *** 1183,1195 **** gettimeofday(&then, 0); /* Reset time counter */ ! } /* end of while loop */ ! /* * program is exiting, this should never run, but is here to make * compiler / valgrind happy */ free_db_list(db_list); ! free_cmd_args(); return EXIT_SUCCESS; } --- 1364,1469 ---- gettimeofday(&then, 0); /* Reset time counter */ ! } /* end of while loop */ ! /* * program is exiting, this should never run, but is here to make * compiler / valgrind happy */ free_db_list(db_list); ! free_cmd_args(); ! return 0; ! } ! ! /* Beginning of AutoVacuum Main Program */ ! int ! main(int argc, char *argv[]) ! { ! ! #ifdef WIN32 ! LPVOID lpMsgBuf; ! SERVICE_TABLE_ENTRY ServiceTable[2]; ! #endif ! ! args = get_cmd_args(argc, argv); /* Get Command Line Args and put ! * them in the args struct */ ! #ifndef WIN32 ! /* Dameonize if requested */ ! if (args->daemonize == 1) ! daemonize(); ! #endif ! ! if (args->logfile) ! { ! LOGOUTPUT = fopen(args->logfile, "a"); ! if (!LOGOUTPUT) ! { ! fprintf(stderr, "Could not open log file - [%s]\n", args->logfile); ! exit(-1); ! } ! } ! else ! LOGOUTPUT = stderr; ! if (args->debug >= 2) ! print_cmd_args(); ! ! #ifdef WIN32 ! /* Install as a Windows service if required */ ! if (args->install_as_service) { ! if(InstallService() != 0) ! { ! FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL); ! sprintf(logbuffer, "Error: %s", (char *)lpMsgBuf); ! log_entry(logbuffer); ! fflush(LOGOUTPUT); ! exit(-1); ! } ! else ! { ! log_entry("INFO: Successfully installed Windows service"); ! fflush(LOGOUTPUT); ! exit(0); ! } ! } ! ! /* Remove as a Windows service if required */ ! if (args->remove_as_service) { ! if(RemoveService() != 0) ! { ! FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL); ! sprintf(logbuffer, "Error: %s", (char *)lpMsgBuf); ! log_entry(logbuffer); ! fflush(LOGOUTPUT); ! exit(-1); ! } ! else ! { ! log_entry("INFO: Successfully removed Windows service"); ! fflush(LOGOUTPUT); ! exit(0); ! } ! } ! ! // Normal service startup ! ServiceTable[0].lpServiceName = "pg_autovacuum"; ! ServiceTable[0].lpServiceProc = (LPSERVICE_MAIN_FUNCTION)VacuumLoop; ! ! ServiceTable[1].lpServiceName = NULL; ! ServiceTable[1].lpServiceProc = NULL; ! ! // Start the control dispatcher thread for our service ! if(!StartServiceCtrlDispatcher(ServiceTable)) { ! log_entry("INFO: pg_autovacuum starting in Windows Application mode"); ! appMode = 1; ! VacuumLoop(0, NULL); ! } ! ! #else /* Unix */ ! ! /* Call the main program loop. */ ! VacuumLoop(0, NULL); ! ! #endif /* WIN32 */ ! return EXIT_SUCCESS; } Only in pg_autovacuum: pg_autovacuum.exe diff -c pg_autovacuum.orig/pg_autovacuum.h pg_autovacuum/pg_autovacuum.h *** pg_autovacuum.orig/pg_autovacuum.h Mon Mar 15 16:17:30 2004 --- pg_autovacuum/pg_autovacuum.h Thu May 6 13:12:55 2004 *************** *** 2,7 **** --- 2,9 ---- * Header file for pg_autovacuum.c * (c) 2003 Matthew T. O'Connor */ + + #define FRONTEND #include "postgres_fe.h" *************** *** 36,42 **** #define TABLE_STATS_QUERY "select a.oid,a.relname,a.relnamespace,a.relpages,a.relisshared,a.reltuples,b.schemaname,b.n_tup_ins,b.n_tup_upd,b.n_tup_del from pg_class a, pg_stat_all_tables b where a.oid=b.relid and a.relkind = 'r'" - #define FRONTEND #define PAGES_QUERY "select oid,reltuples,relpages from pg_class where oid=%u" #define FROZENOID_QUERY "select oid,age(datfrozenxid) from pg_database where datname = 'template1'" #define FROZENOID_QUERY2 "select oid,datname,age(datfrozenxid) from pg_database where datname!='template0'" --- 38,43 ---- *************** *** 50,57 **** int vacuum_base_threshold, analyze_base_threshold, sleep_base_value, ! debug, ! daemonize; float vacuum_scaling_factor, analyze_scaling_factor, sleep_scaling_factor; --- 51,63 ---- int vacuum_base_threshold, analyze_base_threshold, sleep_base_value, ! debug, ! #ifndef WIN32 ! daemonize; ! #else ! install_as_service, ! remove_as_service; ! #endif float vacuum_scaling_factor, analyze_scaling_factor, sleep_scaling_factor; *************** *** 105,110 **** --- 111,119 ---- * belongs to */ }; typedef struct tableinfo tbl_info; + + /* The main program loop function */ + static int VacuumLoop(int argc, char** argv); /* Functions for dealing with command line arguements */ static cmd_args *get_cmd_args(int argc, char *argv[]); *************** *** 137,142 **** static void db_disconnect(db_info * dbi); static PGresult *send_query(const char *query, db_info * dbi); ! /* Other Generally needed Functions */ ! static void daemonize(void); ! static void log_entry(const char *logentry); --- 146,162 ---- static void db_disconnect(db_info * dbi); static PGresult *send_query(const char *query, db_info * dbi); ! /* Other Generally needed Functions */ ! #ifndef WIN32 ! static void daemonize(void); ! #endif ! static void log_entry(const char *logentry); ! ! #ifdef WIN32 ! /* Windows Service related functions */ ! static void ControlHandler(DWORD request); ! static int InstallService(); ! static int RemoveService(); ! #endif ! !