/* Copyright (c) 2009, Perforce Software, Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL PERFORCE SOFTWARE, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* User contributed content on the Perforce Public Depot is not supported by Perforce, although it may be supported by its author. This applies to all contributions even those submitted by Perforce employees. */ //////////////////////////////////////////////////////////////////////////////// // // Perforce server trigger program to do low disk-space checking. // // Install it as a "change-submit" trigger to prevent people from adding files // when disk space is low. Note that space can still be taken up by other // commands that add to the log and journal files. // // To compile with GCC on unix platforms: // [] g++ -static -o space_check space_check.cc // // On Windows, // [] cl space_check.cc // // This file must be +k (ktext) for keyword expansion to work on the program Id. // //////////////////////////////////////////////////////////////////////////////// #include <stdlib.h> #include <stdarg.h> #include <stdio.h> #include <string.h> #include <ctype.h> #include <errno.h> #include <sys/stat.h> #ifdef _WIN32 #include <windows.h> #else #include <sys/statvfs.h> #endif #if defined( __APPLE_CC__ ) || defined( __FreeBSD__ ) #include <sys/param.h> #include <sys/mount.h> #endif const char *prog_id = "$Id: //guest/jason_gibson/misc/space_check.cc#6 $"; char *prog_name = NULL, *contact = NULL; void report( const char *fmt, ... ) { va_list args; va_start( args, fmt ); vprintf( fmt, args ); // add extra logging here. va_end( args ); } void print_error_header() { report( "\nSubmit *ABORTED* --- " "Please *CONTACT* the administrator at: %s\n\n" "Trigger program path: %s\n" "Depot ID: %s\n", contact, prog_name, prog_id ); } #ifdef _WIN32 unsigned long ATOI64( const char *v ) { return _atoi64( v ); } #else unsigned long ATOI64( const char *v ) { return strtoul( v, NULL, 10 ); } #endif // Return the number of free bytes available to the calling user. unsigned long long get_free_space( const char* where ) { #ifdef _WIN32 ULARGE_INTEGER lpFreeBytesAvailable, lpTotalNumberOfBytes, lpTotalNumberOfFreeBytes; if( !GetDiskFreeSpaceEx( where, &lpFreeBytesAvailable, &lpTotalNumberOfBytes, &lpTotalNumberOfFreeBytes ) ) { report( "GetDiskFreeSpaceEx() failed: %s\n%s\n", GetLastError(), where ); print_error_header(); exit( 1 ); } return lpFreeBytesAvailable.QuadPart; #else # ifdef __APPLE_CC__ struct statfs64 df; if( statfs64( where, &df ) == -1 ) # else # ifdef __FreeBSD__ struct statfs df; if( statfs( where, &df ) == -1 ) # else struct statvfs df; if( statvfs( where, &df ) == -1 ) # endif # endif { report( "stat(v)fs() failed: %s\n%s\n", strerror( errno ), where ); print_error_header(); exit( 1 ); } // block size * available blocks return ((unsigned long long)df.f_bsize) * ((unsigned long long)df.f_bavail); #endif } int main( int argc, char* argv[] ) { prog_name = argv[ 0 ]; unsigned int arg_offset = 1; bool reporting = false; if( argc > 1 && !strcmp( argv[ 1 ], "-r" ) ) { reporting = true; arg_offset += 1; } contact = argv[ arg_offset ]; arg_offset += 1; if( argc < 4 || (argc + arg_offset) % 2 ) { #ifdef _WIN32 const char *p1 = "c:\\p4_db\\"; const char *p2 = "d:\\p4_depot\\"; const char *p3 = "e:\\p4_logs\\"; const char *p4 = "d:\\p4_triggers\\"; #else const char *p1 = "/disk1/p4_db/"; const char *p2 = "/disk2/p4_depot/"; const char *p3 = "/disk3/p4_logs/"; const char *p4 = "/disk4/p4_triggers/"; #endif report( "Perforce server low disk-space checking trigger.\n\n" "USAGE: space_check (-r) admin_info path1 size1 ... pathN sizeN\n" "EXAMPLE: space_check admin@example.com\n" " %s 500M\n" " %s 5G\n" " %s 100M\n\n" "Available units of size are (M)egabytes and (G)igabytes.\n" "Use the '-r' argument to report values while debugging.\n" "Use an absolute path in the trigger definition for the\n" "best error reporting. UNC paths and drive letters on\n" "Windows must have a trailing slash.\n\n" "Example trigger definition:\n\n" "\tTriggers:\n" "\t space_check change-submit //... \"%s" "space_check" " admin@example.com %s 10G\"\n\n", p1, p2, p3, p4, p1 ); return 1; } const unsigned long MEGABYTES = 1048576; // 1024 ^ 2 const unsigned long GIGABYTES = 1073741824; // 3 bool has_error = false; for( int i = arg_offset; argv[ i ]; i += 2 ) { const char *where = argv[ i ]; const char *what = argv[ i + 1 ]; const unsigned long long min_space = ATOI64( what ); const char unit = toupper( what[ strlen( what ) - 1 ] ); unsigned long long free_bytes = get_free_space( where ), free_space = 0; switch( unit ) { case 'M': free_space = free_bytes / MEGABYTES; break; case 'G': free_space = free_bytes / GIGABYTES; break; default: report( "Unknown unit: %c\n", unit ); print_error_header(); return 1; } if( reporting ) report( "Location : %s \n" #ifdef _WIN32 "Free space : %I64u (in bytes: %I64u )\n" #else "Free space : %llu (in bytes: %llu )\n" #endif "Min space : %s \n\n", where, free_space, free_bytes, what ); if( free_space <= min_space ) { if( !has_error ) report( "*WARNING* ======================== *WARNING*\n\n" ); report( "Minimum allowed disk space on \"%s\" is %s - " #ifdef _WIN32 "currently at: %I64u\n", #else "currently at: %llu\n", #endif where, what, free_space ); has_error = true; } } if( has_error ) { print_error_header(); return 1; } return 0; } // eof
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#6 | 7494 | Jason Gibson | Add license headers. | ||
#5 | 7403 | Jason Gibson |
Use an explicit cast for the return value calculation for free space. Bug fix thanks to Benoit Maury-Bouet. |
||
#4 | 7215 | Jason Gibson | ifdef fun | ||
#3 | 7214 | Jason Gibson | support FreeBSD | ||
#2 | 7213 | Jason Gibson | correct the example trigger definition | ||
#1 | 7212 | Jason Gibson | server trigger to check for the low disk-space condition |