debianqueued 72 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427
  1. #!/usr/bin/perl -w
  2. #
  3. # debianqueued -- daemon for managing Debian upload queues
  4. #
  5. # Copyright (C) 1997 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
  6. # Copyright (C) 2001-2007 Ryan Murray <rmurray@debian.org>
  7. # Copyright (C) 2008 Thomas Viehmann <tv@beamnet.de>
  8. #
  9. # This program is free software. You can redistribute it and/or
  10. # modify it under the terms of the GNU General Public License as
  11. # published by the Free Software Foundation: either version 2 or
  12. # (at your option) any later version.
  13. # This program comes with ABSOLUTELY NO WARRANTY!
  14. #
  15. require 5.002;
  16. no lib '.';
  17. use strict;
  18. use List::Util;
  19. use POSIX;
  20. use POSIX qw( strftime sys_stat_h sys_wait_h signal_h );
  21. use Net::Ping;
  22. use Net::FTP;
  23. use Socket qw( PF_INET AF_INET SOCK_STREAM );
  24. use Config;
  25. use Sys::Hostname;
  26. use File::Copy;
  27. use Digest::MD5;
  28. setlocale(&POSIX::LC_ALL, "C");
  29. $ENV{"LC_ALL"} = "C";
  30. # ---------------------------------------------------------------------------
  31. # configuration
  32. # ---------------------------------------------------------------------------
  33. package conf;
  34. ( $conf::queued_dir = ( ( $0 !~ m,^/, ) ? POSIX::getcwd() . "/" : "" ) . $0 )
  35. =~ s,/[^/]+$,,;
  36. # various programs:
  37. our $gpg = "/usr/bin/gpg";
  38. our $ssh = "/usr/bin/ssh";
  39. our $scp = "/usr/bin/scp";
  40. our $ssh_agent = "/usr/bin/ssh-agent";
  41. our $ssh_add = "/usr/bin/ssh-add";
  42. our $mail = "/usr/sbin/sendmail";
  43. # default umask:
  44. # This is mostly for the "copy" upload method. Logs, pidfile get
  45. # explicit permissions via `chmod`.
  46. our $umask = 0022;
  47. require "$conf::queued_dir/config";
  48. my $junk = $conf::debug; # avoid spurious warnings about unused vars
  49. $junk = $conf::ssh_key_file;
  50. $junk = $conf::stray_remove_timeout;
  51. $junk = $conf::problem_report_timeout;
  52. $junk = $conf::queue_delay;
  53. $junk = $conf::keep_files;
  54. $junk = $conf::valid_files;
  55. $junk = $conf::max_upload_retries;
  56. $junk = $conf::upload_delay_1;
  57. $junk = $conf::upload_delay_2;
  58. $junk = $conf::check_md5sum;
  59. $junk = $conf::ftpdebug;
  60. $junk = $conf::ftptimeout;
  61. $junk = @conf::nonus_packages;
  62. $junk = @conf::maintainer_mail;
  63. $junk = @conf::targetdir_delayed;
  64. $junk = $conf::mail ||= '/usr/sbin/sendmail';
  65. $junk = $conf::overridemail;
  66. $conf::target = "localhost" if $conf::upload_method eq "copy";
  67. package main;
  68. if (defined $conf::umask) {
  69. umask $conf::umask;
  70. }
  71. ( $main::progname = $0 ) =~ s,.*/,,;
  72. ($main::hostname, undef, undef, undef, undef) = gethostbyname(hostname());
  73. my %packages = ();
  74. my $re_file_safe_prefix = qr/\A([a-zA-Z0-9.][a-zA-Z0-9_.:~+-]*)/s;
  75. my $re_file_safe = qr/$re_file_safe_prefix\z/s;
  76. # extract -r and -k args
  77. $main::arg = "";
  78. if ( @ARGV == 1 && $ARGV[0] =~ /^-[rk]$/ ) {
  79. $main::arg = ( $ARGV[0] eq '-k' ) ? "kill" : "restart";
  80. shift @ARGV;
  81. }
  82. # test for another instance of the queued already running
  83. my ( $pid, $delayed_dirs, $adelayedcore );
  84. if ( open( PIDFILE, "<", $conf::pidfile ) ) {
  85. chomp( $pid = <PIDFILE> );
  86. close(PIDFILE);
  87. if ( !$pid ) {
  88. # remove stale pid file
  89. unlink($conf::pidfile);
  90. } elsif ($main::arg) {
  91. local ($|) = 1;
  92. print "Killing running daemon (pid $pid) ...";
  93. kill( 15, $pid );
  94. my $cnt = 20;
  95. while ( kill( 0, $pid ) && $cnt-- > 0 ) {
  96. sleep 1;
  97. print ".";
  98. }
  99. if ( kill( 0, $pid ) ) {
  100. print " failed!\nProcess $pid still running.\n";
  101. exit 1;
  102. }
  103. print "ok\n";
  104. if ( -e "$conf::incoming/core" ) {
  105. unlink("$conf::incoming/core");
  106. print "(Removed core file)\n";
  107. }
  108. for ( $delayed_dirs = 0 ;
  109. $delayed_dirs <= $conf::max_delayed ;
  110. $delayed_dirs++ )
  111. {
  112. $adelayedcore =
  113. sprintf( "$conf::incoming_delayed/core", $delayed_dirs );
  114. if ( -e $adelayedcore ) {
  115. unlink($adelayedcore);
  116. print "(Removed core file)\n";
  117. }
  118. } ## end for ( $delayed_dirs = 0...
  119. exit 0 if $main::arg eq "kill";
  120. } else {
  121. die "Another $main::progname is already running (pid $pid)\n"
  122. if $pid && kill( 0, $pid );
  123. }
  124. } elsif ( $main::arg eq "kill" ) {
  125. die "No daemon running\n";
  126. } elsif ( $main::arg eq "restart" ) {
  127. print "(No daemon running; starting anyway)\n";
  128. }
  129. # if started without arguments (initial invocation), then fork
  130. if ( !@ARGV ) {
  131. # now go to background
  132. die "$main::progname: fork failed: $!\n"
  133. unless defined( $pid = fork );
  134. if ($pid) {
  135. # parent: wait for signal from child (SIGCHLD or SIGUSR1) and exit
  136. my $sigset = POSIX::SigSet->new();
  137. $sigset->emptyset();
  138. $SIG{"CHLD"} = sub { };
  139. $SIG{"USR1"} = sub { };
  140. POSIX::sigsuspend($sigset);
  141. waitpid( $pid, WNOHANG );
  142. if ( kill( 0, $pid ) ) {
  143. print "Daemon (on $main::hostname) started in background (pid $pid)\n";
  144. exit 0;
  145. } else {
  146. exit 1;
  147. }
  148. } else {
  149. # child
  150. setsid;
  151. if ( $conf::upload_method eq "ssh" ) {
  152. # exec an ssh-agent that starts us again
  153. # force shell to be /bin/sh, ssh-agent may base its decision
  154. # whether to use a fd or a Unix socket on the shell...
  155. $ENV{"SHELL"} = "/bin/sh";
  156. exec $conf::ssh_agent, $0, "startup", getppid();
  157. die "$main::progname: Could not exec $conf::ssh_agent: $!\n";
  158. } else {
  159. # no need to exec, just set up @ARGV as expected below
  160. @ARGV = ( "startup", getppid() );
  161. }
  162. } ## end else [ if ($pid)
  163. } ## end if ( !@ARGV )
  164. die "Please start without any arguments.\n"
  165. if @ARGV != 2 || $ARGV[0] ne "startup";
  166. my $parent_pid = $ARGV[1];
  167. do {
  168. my $version;
  169. ( $version = 'Release: 0.95' ) =~ s/\$ ?//g;
  170. print "debianqueued $version\n";
  171. };
  172. # check if all programs exist
  173. my $prg;
  174. foreach $prg ( $conf::gpg, $conf::ssh, $conf::scp, $conf::ssh_agent,
  175. $conf::ssh_add, $conf::mail )
  176. {
  177. die "Required program $prg doesn't exist or isn't executable\n"
  178. if !-x $prg;
  179. # check for correct upload method
  180. die "Bad upload method '$conf::upload_method'.\n"
  181. if $conf::upload_method ne "ssh"
  182. && $conf::upload_method ne "ftp"
  183. && $conf::upload_method ne "copy";
  184. die "No keyrings\n" if !@conf::keyrings;
  185. } ## end foreach $prg ( $conf::gpg, ...
  186. die "statusfile path must be absolute."
  187. if $conf::statusfile !~ m,^/,;
  188. die "upload and target queue paths must be absolute."
  189. if $conf::incoming !~ m,^/,
  190. || $conf::incoming_delayed !~ m,^/,
  191. || $conf::targetdir !~ m,^/,
  192. || $conf::targetdir_delayed !~ m,^/,;
  193. # ---------------------------------------------------------------------------
  194. # initializations
  195. # ---------------------------------------------------------------------------
  196. # prototypes
  197. sub calc_delta();
  198. sub check_dir();
  199. sub get_filelist_from_known_good_changes($);
  200. sub age_delayed_queues();
  201. sub process_changes($\@);
  202. sub process_commands($);
  203. sub age_delayed_queues();
  204. sub is_on_target($\@);
  205. sub copy_to_target(@);
  206. sub pgp_check($);
  207. sub check_alive(;$);
  208. sub check_incoming_writable();
  209. sub fork_statusd();
  210. sub write_status_file();
  211. sub print_status($$$$$$);
  212. sub format_status_num(\$$);
  213. sub format_status_str(\$$);
  214. sub send_status();
  215. sub ftp_open();
  216. sub ftp_cmd($@);
  217. sub ftp_close();
  218. sub ftp_response();
  219. sub ftp_code();
  220. sub ftp_error();
  221. sub ssh_cmd($);
  222. sub scp_cmd(@);
  223. sub check_alive(;$);
  224. sub check_incoming_writable();
  225. sub rm(@);
  226. sub md5sum($);
  227. sub msg($@);
  228. sub debug(@);
  229. sub init_mail(;$);
  230. sub finish_mail();
  231. sub send_mail($$$);
  232. sub try_to_get_mail_addr($$);
  233. sub format_time();
  234. sub print_time($);
  235. sub block_signals();
  236. sub unblock_signals();
  237. sub close_log($);
  238. sub kid_died($);
  239. sub restart_statusd();
  240. sub fatal_signal($);
  241. $ENV{"PATH"} = "/bin:/usr/bin";
  242. $ENV{"IFS"} = "" if defined( $ENV{"IFS"} && $ENV{"IFS"} ne "" );
  243. # constants for stat
  244. sub ST_DEV() { 0 }
  245. sub ST_INO() { 1 }
  246. sub ST_MODE() { 2 }
  247. sub ST_NLINK() { 3 }
  248. sub ST_UID() { 4 }
  249. sub ST_GID() { 5 }
  250. sub ST_RDEV() { 6 }
  251. sub ST_SIZE() { 7 }
  252. sub ST_ATIME() { 8 }
  253. sub ST_MTIME() { 9 }
  254. sub ST_CTIME() { 10 }
  255. # fixed lengths of data items passed over status pipe
  256. sub STATNUM_LEN() { 30 }
  257. sub STATSTR_LEN() { 128 }
  258. # init list of signals
  259. defined $Config{sig_name}
  260. or die "$main::progname: No signal list defined!\n";
  261. my $i = 0;
  262. my $name;
  263. foreach $name ( split( ' ', $Config{sig_name} ) ) {
  264. $main::signo{$name} = $i++;
  265. }
  266. @main::fatal_signals = qw( INT QUIT ILL TRAP ABRT BUS FPE USR2 SEGV PIPE
  267. TERM XCPU XFSZ PWR );
  268. $main::block_sigset = POSIX::SigSet->new;
  269. $main::block_sigset->addset( $main::signo{"INT"} );
  270. $main::block_sigset->addset( $main::signo{"TERM"} );
  271. # some constant net stuff
  272. $main::tcp_proto = ( getprotobyname('tcp') )[2]
  273. or die "Cannot get protocol number for 'tcp'\n";
  274. my $used_service = ( $conf::upload_method eq "ssh" ) ? "ssh" : "ftp";
  275. $main::echo_port = ( getservbyname( $used_service, 'tcp' ) )[2]
  276. or die "Cannot get port number for service '$used_service'\n";
  277. # clear queue of stored mails
  278. @main::stored_mails = ();
  279. # run ssh-add to bring the key into the agent (will use stdin/stdout)
  280. if ( $conf::upload_method eq "ssh" ) {
  281. system "$conf::ssh_add $conf::ssh_key_file"
  282. and die "$main::progname: Running $conf::ssh_add failed "
  283. . "(exit status ", $? >> 8, ")\n";
  284. }
  285. # change to queue dir
  286. chdir($conf::incoming)
  287. or die "$main::progname: cannot cd to $conf::incoming: $!\n";
  288. # needed before /dev/null redirects, some system send a SIGHUP when loosing
  289. # the controlling tty
  290. $SIG{"HUP"} = "IGNORE";
  291. # open logfile, make it unbuffered
  292. open( LOG, ">>", $conf::logfile )
  293. or die "Cannot open my logfile $conf::logfile: $!\n";
  294. chmod( 0644, $conf::logfile )
  295. or die "Cannot set modes of $conf::logfile: $!\n";
  296. select( ( select(LOG), $| = 1 )[0] );
  297. sleep(1);
  298. $SIG{"HUP"} = \&close_log;
  299. # redirect stdin, ... to /dev/null
  300. open( STDIN, "<", "/dev/null" )
  301. or die "$main::progname: Can't redirect stdin to /dev/null: $!\n";
  302. open( STDOUT, ">&", \*LOG )
  303. or die "$main::progname: Can't redirect stdout to $conf::logfile: $!\n";
  304. open( STDERR, ">&", \*LOG )
  305. or die "$main::progname: Can't redirect stderr to $conf::logfile: $!\n";
  306. # ok, from this point usually no "die" anymore, stderr is gone!
  307. msg( "log", "daemon (pid $$) (on $main::hostname) started\n" );
  308. # initialize variables used by send_status before launching the status daemon
  309. $main::dstat = "i";
  310. format_status_num( $main::next_run, time + 10 );
  311. format_status_str( $main::current_changes, "" );
  312. check_alive();
  313. $main::incoming_writable = 1; # assume this for now
  314. # start the daemon watching the 'status' FIFO
  315. if ( $conf::statusfile && $conf::statusdelay == 0 ) {
  316. $main::statusd_pid = fork_statusd();
  317. $SIG{"CHLD"} = \&kid_died; # watch out for dead status daemon
  318. # SIGUSR1 triggers status info
  319. $SIG{"USR1"} = \&send_status;
  320. } ## end if ( $conf::statusfile...
  321. $main::maind_pid = $$;
  322. END {
  323. kill( $main::signo{"ABRT"}, $$ )
  324. if defined $main::signo{"ABRT"};
  325. }
  326. # write the pid file
  327. open( PIDFILE, ">", $conf::pidfile )
  328. or msg( "log", "Can't open $conf::pidfile: $!\n" );
  329. printf PIDFILE "%5d\n", $$;
  330. close(PIDFILE);
  331. chmod( 0644, $conf::pidfile )
  332. or die "Cannot set modes of $conf::pidfile: $!\n";
  333. # other signals will just log an error and exit
  334. foreach (@main::fatal_signals) {
  335. $SIG{$_} = \&fatal_signal;
  336. }
  337. # send signal to user-started process that we're ready and it can exit
  338. kill( $main::signo{"USR1"}, $parent_pid );
  339. # ---------------------------------------------------------------------------
  340. # the mainloop
  341. # ---------------------------------------------------------------------------
  342. # default to classical incoming/target
  343. $main::current_incoming = $conf::incoming;
  344. $main::current_targetdir = $conf::targetdir;
  345. $main::dstat = "i";
  346. write_status_file() if $conf::statusdelay;
  347. while (1) {
  348. # ping target only if there is the possibility that we'll contact it (but
  349. # also don't wait too long).
  350. my @have_changes = <*.changes *.commands *.dak-commands>;
  351. for ( my $delayed_dirs = 0 ;
  352. $delayed_dirs <= $conf::max_delayed ;
  353. $delayed_dirs++ )
  354. {
  355. my $adelayeddir = sprintf( "$conf::incoming_delayed", $delayed_dirs );
  356. push( @have_changes, <$adelayeddir/*.changes> );
  357. } ## end for ( my $delayed_dirs ...
  358. check_alive()
  359. if @have_changes || ( time - $main::last_ping_time ) > 8 * 60 * 60;
  360. if ( @have_changes && $main::target_up ) {
  361. check_incoming_writable if !$main::incoming_writable;
  362. check_dir() if $main::incoming_writable;
  363. }
  364. $main::dstat = "i";
  365. write_status_file() if $conf::statusdelay;
  366. if ( $conf::upload_method eq "copy" ) {
  367. age_delayed_queues();
  368. }
  369. # sleep() returns if we received a signal (SIGUSR1 for status FIFO), so
  370. # calculate the end time once and wait for it being reached.
  371. format_status_num( $main::next_run, time + $conf::queue_delay );
  372. my $delta;
  373. while ( ( $delta = calc_delta() ) > 0 ) {
  374. debug("mainloop sleeping $delta secs");
  375. sleep($delta);
  376. # check if statusd died, if using status FIFO, or update status file
  377. if ($conf::statusdelay) {
  378. write_status_file();
  379. } else {
  380. restart_statusd();
  381. }
  382. } ## end while ( ( $delta = calc_delta...
  383. } ## end while (1)
  384. sub calc_delta() {
  385. my $delta;
  386. $delta = $main::next_run - time;
  387. $delta = $conf::statusdelay
  388. if $conf::statusdelay && $conf::statusdelay < $delta;
  389. return $delta;
  390. } ## end sub calc_delta()
  391. # ---------------------------------------------------------------------------
  392. # main working functions
  393. # ---------------------------------------------------------------------------
  394. #
  395. # main function for checking the incoming dir
  396. #
  397. sub check_dir() {
  398. my ( @files, @changes, @keep_files, @this_keep_files, @stats, $file,
  399. $adelay );
  400. debug("starting checkdir");
  401. $main::dstat = "c";
  402. write_status_file() if $conf::statusdelay;
  403. for ( $adelay = -1 ; $adelay <= $conf::max_delayed ; $adelay++ ) {
  404. if ( $adelay == -1 ) {
  405. $main::current_incoming = $conf::incoming;
  406. $main::current_incoming_short = "";
  407. $main::current_targetdir = $conf::targetdir;
  408. } else {
  409. $main::current_incoming = sprintf( $conf::incoming_delayed, $adelay );
  410. $main::current_incoming_short = sprintf( "DELAYED/%d-day", $adelay );
  411. $main::current_targetdir = sprintf( $conf::targetdir_delayed, $adelay );
  412. }
  413. # need to clear directory specific variables
  414. undef(@keep_files);
  415. undef(@this_keep_files);
  416. chdir($main::current_incoming)
  417. or (
  418. msg(
  419. "log",
  420. "Cannot change to dir "
  421. . "${main::current_incoming_short}: $!\n"
  422. ),
  423. return
  424. );
  425. # look for *.commands and *.dak-commands files but not in delayed queues
  426. if ( $adelay == -1 ) {
  427. foreach $file (<*.commands>) {
  428. next unless $file =~ /$re_file_safe/;
  429. init_mail($file);
  430. block_signals();
  431. process_commands($file);
  432. unblock_signals();
  433. $main::dstat = "c";
  434. write_status_file() if $conf::statusdelay;
  435. finish_mail();
  436. } ## end foreach $file (<*.commands>)
  437. foreach $file (<*.dak-commands>) {
  438. next unless $file =~ /$re_file_safe/;
  439. init_mail($file);
  440. block_signals();
  441. process_dak_commands($file);
  442. unblock_signals();
  443. $main::dstat = "c";
  444. write_status_file() if $conf::statusdelay;
  445. finish_mail();
  446. }
  447. } ## end if ( $adelay == -1 )
  448. opendir( INC, "." )
  449. or (
  450. msg(
  451. "log", "Cannot open dir ${main::current_incoming_short}: $!\n"
  452. ),
  453. return
  454. );
  455. @files = readdir(INC);
  456. closedir(INC);
  457. # process all .changes files found
  458. @changes = grep /\.changes$/, @files;
  459. push( @keep_files, @changes ); # .changes files aren't stray
  460. foreach $file (@changes) {
  461. next unless $file =~ /$re_file_safe/;
  462. init_mail($file);
  463. # wrap in an eval to allow jumpbacks to here with die in case
  464. # of errors
  465. block_signals();
  466. eval { process_changes( $file, @this_keep_files ); };
  467. unblock_signals();
  468. msg( "log,mail", $@ ) if $@;
  469. $main::dstat = "c";
  470. write_status_file() if $conf::statusdelay;
  471. # files which are ok in conjunction with this .changes
  472. debug("$file tells to keep @this_keep_files");
  473. push( @keep_files, @this_keep_files );
  474. finish_mail();
  475. # break out of this loop if the incoming dir has become unwritable
  476. goto end_run if !$main::incoming_writable;
  477. } ## end foreach $file (@changes)
  478. ftp_close() if $conf::upload_method eq "ftp";
  479. # find files which aren't related to any .changes
  480. foreach $file (@files) {
  481. # filter out files we never want to delete
  482. next if !-f $file || # may have disappeared in the meantime
  483. $file eq "."
  484. || $file eq ".."
  485. || ( grep { $_ eq $file } @keep_files )
  486. || $file =~ /$conf::keep_files/;
  487. # Delete such files if they're older than
  488. # $stray_remove_timeout; they could be part of an
  489. # yet-incomplete upload, with the .changes still missing.
  490. # Cannot send any notification, since owner unknown.
  491. next if !( @stats = stat($file) );
  492. my $cmtime = List::Util::max($stats[ST_CTIME], $stats[ST_MTIME]);
  493. my $age = time - $cmtime;
  494. my ( $maint, $pattern, @job_files );
  495. if ( $file =~ /^junk-for-writable-test/
  496. || $file !~ m,$conf::valid_files,
  497. || $file !~ /$re_file_safe/
  498. || $age >= $conf::stray_remove_timeout )
  499. {
  500. msg( "log",
  501. "Deleted stray file ${main::current_incoming_short}/$file\n" )
  502. if rm($file);
  503. } else {
  504. debug(
  505. "found stray file ${main::current_incoming_short}/$file, deleting in ",
  506. print_time( $conf::stray_remove_timeout - $age )
  507. );
  508. } ## end else [ if ( $file =~ /^junk-for-writable-test/...
  509. } ## end foreach $file (@files)
  510. } ## end for ( $adelay = -1 ; $adelay...
  511. chdir($conf::incoming);
  512. end_run:
  513. $main::dstat = "i";
  514. write_status_file() if $conf::statusdelay;
  515. } ## end sub check_dir()
  516. sub get_filelist_from_known_good_changes($) {
  517. my $changes = shift;
  518. local (*CHANGES);
  519. my (@filenames);
  520. # parse the .changes file
  521. open( CHANGES, "<", $changes )
  522. or die "$changes: $!\n";
  523. outer_loop: while (<CHANGES>) {
  524. if (/^Files:/i) {
  525. while (<CHANGES>) {
  526. redo outer_loop if !/^\s/;
  527. my @field = split(/\s+/);
  528. next if @field != 6;
  529. # forbid shell meta chars in the name, we pass it to a
  530. # subshell several times...
  531. $field[5] =~ /$re_file_safe/;
  532. if ( $1 ne $field[5] ) {
  533. msg( "log", "found suspicious filename $field[5]\n" );
  534. next;
  535. }
  536. push( @filenames, $field[5] );
  537. } ## end while (<CHANGES>)
  538. } ## end if (/^Files:/i)
  539. } ## end while (<CHANGES>)
  540. close(CHANGES);
  541. return @filenames;
  542. } ## end sub get_filelist_from_known_good_changes($)
  543. #
  544. # process one .changes file
  545. #
  546. sub process_changes($\@) {
  547. my $changes = shift;
  548. my $keep_list = shift;
  549. my (
  550. $pgplines, @files, @filenames, @changes_stats,
  551. $failure_file, $retries, $last_retry, $upload_time,
  552. $file, $do_report, $ls_l,
  553. $errs, $pkgname, $signator, $extralines
  554. );
  555. local (*CHANGES);
  556. local (*FAILS);
  557. format_status_str( $main::current_changes,
  558. "$main::current_incoming_short/$changes" );
  559. $main::dstat = "c";
  560. $main::mail_addr = "";
  561. write_status_file() if $conf::statusdelay;
  562. @$keep_list = ();
  563. msg( "log", "processing ${main::current_incoming_short}/$changes\n" );
  564. # run PGP on the file to check the signature
  565. if ( !( $signator = pgp_check($changes) ) ) {
  566. msg(
  567. "log,mail",
  568. "$main::current_incoming_short/$changes has bad PGP/GnuPG signature!\n"
  569. );
  570. goto remove_only_changes;
  571. } elsif ( $signator eq "LOCAL ERROR" ) {
  572. # An error has appened when starting pgp... Don't process the file,
  573. # but also don't delete it
  574. debug(
  575. "Can't PGP/GnuPG check $main::current_incoming_short/$changes -- don't process it for now"
  576. );
  577. return;
  578. } ## end elsif ( $signator eq "LOCAL ERROR")
  579. # parse the .changes file
  580. open( CHANGES, "<", $changes )
  581. or die "Cannot open ${main::current_incoming_short}/$changes: $!\n";
  582. $pgplines = 0;
  583. $extralines = 0;
  584. @files = ();
  585. outer_loop: while (<CHANGES>) {
  586. if (/^---+(BEGIN|END) PGP .*---+$/) {
  587. ++$pgplines;
  588. next;
  589. }
  590. if ( $pgplines < 1 or $pgplines >= 3 ) {
  591. $extralines++ if length $_ > 1;
  592. next;
  593. }
  594. if ($pgplines != 1) {
  595. next;
  596. }
  597. if (/^Maintainer:\s*/i) {
  598. chomp( $main::mail_addr = $' );
  599. $main::mail_addr = $1 if $main::mail_addr =~ /<([^>]*)>/;
  600. } elsif (/^Source:\s*/i) {
  601. chomp( $pkgname = $' );
  602. $pkgname =~ s/\s+$//;
  603. $main::packages{$pkgname}++;
  604. } elsif (/^Files:/i) {
  605. while (<CHANGES>) {
  606. redo outer_loop if !/^\s/;
  607. my @field = split(/\s+/);
  608. next if @field != 6;
  609. # forbid shell meta chars in the name, we pass it to a
  610. # subshell several times...
  611. $field[5] =~ /$re_file_safe/;
  612. if ( $1 ne $field[5] ) {
  613. msg( "log", "found suspicious filename $field[5]\n" );
  614. msg(
  615. "mail",
  616. "File '$field[5]' mentioned in $main::current_incoming_short/$changes\n",
  617. "has bad characters in its name. Removed.\n"
  618. );
  619. rm( $field[5] );
  620. next;
  621. } ## end if ( $1 ne $field[5] )
  622. push(
  623. @files,
  624. {
  625. md5 => $field[1],
  626. size => $field[2],
  627. name => $field[5]
  628. }
  629. );
  630. push( @filenames, $field[5] );
  631. debug( "includes file $field[5], size $field[2], ", "md5 $field[1]" );
  632. } ## end while (<CHANGES>)
  633. } ## end elsif (/^Files:/i)
  634. } ## end while (<CHANGES>)
  635. close(CHANGES);
  636. # tell check_dir that the files mentioned in this .changes aren't stray,
  637. # we know about them somehow
  638. @$keep_list = @filenames;
  639. # some consistency checks
  640. if ( $extralines ) {
  641. msg( "log,mail",
  642. "$main::current_incoming_short/$changes contained lines outside the pgp signed "
  643. ."part, cannot process\n" );
  644. goto remove_only_changes;
  645. } ## end if ( $extralines )
  646. if ( !$main::mail_addr ) {
  647. msg( "log,mail",
  648. "$main::current_incoming_short/$changes doesn't contain a Maintainer: field; "
  649. . "cannot process\n" );
  650. goto remove_only_changes;
  651. } ## end if ( !$main::mail_addr)
  652. if ( $main::mail_addr !~ /^(buildd_\S+-\S+|\S+\@\S+\.\S+)/ ) {
  653. # doesn't look like a mail address, maybe only the name
  654. my ( $new_addr, @addr_list );
  655. if ( $new_addr = try_to_get_mail_addr( $main::mail_addr, \@addr_list ) ) {
  656. # substitute (unique) found addr, but give a warning
  657. msg(
  658. "mail",
  659. "(The Maintainer: field didn't contain a proper "
  660. . "mail address.\n"
  661. );
  662. msg(
  663. "mail",
  664. "Looking for `$main::mail_addr' in the Debian "
  665. . "keyring gave your address\n"
  666. );
  667. msg( "mail", "as unique result, so I used this.)\n" );
  668. msg( "log",
  669. "Substituted $new_addr for malformed " . "$main::mail_addr\n" );
  670. $main::mail_addr = $new_addr;
  671. } else {
  672. # not found or not unique: hold the job and inform queue maintainer
  673. my $old_addr = $main::mail_addr;
  674. $main::mail_addr = $conf::maintainer_mail;
  675. msg(
  676. "mail",
  677. "The job ${main::current_incoming_short}/$changes doesn't have a correct email\n"
  678. );
  679. msg( "mail", "address in the Maintainer: field:\n" );
  680. msg( "mail", " $old_addr\n" );
  681. msg( "mail", "A check for this in the Debian keyring gave:\n" );
  682. msg( "mail",
  683. @addr_list
  684. ? " " . join( ", ", @addr_list ) . "\n"
  685. : " nothing\n" );
  686. msg( "mail", "Please fix this manually\n" );
  687. msg(
  688. "log",
  689. "Bad Maintainer: field in ${main::current_incoming_short}/$changes: $old_addr\n"
  690. );
  691. goto remove_only_changes;
  692. } ## end else [ if ( $new_addr = try_to_get_mail_addr...
  693. } ## end if ( $main::mail_addr ...
  694. if ( $pgplines < 3 ) {
  695. msg(
  696. "log,mail",
  697. "$main::current_incoming_short/$changes isn't signed with PGP/GnuPG\n"
  698. );
  699. msg( "log", "(uploader $main::mail_addr)\n" );
  700. goto remove_only_changes;
  701. } ## end if ( $pgplines < 3 )
  702. if ( !@files ) {
  703. msg( "log,mail",
  704. "$main::current_incoming_short/$changes doesn't mention any files\n" );
  705. msg( "log", "(uploader $main::mail_addr)\n" );
  706. goto remove_only_changes;
  707. } ## end if ( !@files )
  708. # check for packages that shouldn't be processed
  709. if ( grep( $_ eq $pkgname, @conf::nonus_packages ) ) {
  710. msg(
  711. "log,mail",
  712. "$pkgname is a package that must be uploaded "
  713. . "to nonus.debian.org\n"
  714. );
  715. msg( "log,mail", "instead of target.\n" );
  716. msg( "log,mail",
  717. "Job rejected and removed all files belonging " . "to it:\n" );
  718. msg( "log,mail", " ", join( ", ", @filenames ), "\n" );
  719. rm( $changes, @filenames );
  720. return;
  721. } ## end if ( grep( $_ eq $pkgname...
  722. $failure_file = $changes . ".failures";
  723. $retries = $last_retry = 0;
  724. if ( -f $failure_file ) {
  725. open( FAILS, "<", $failure_file )
  726. or die "Cannot open $main::current_incoming_short/$failure_file: $!\n";
  727. my $line = <FAILS>;
  728. close(FAILS);
  729. ( $retries, $last_retry ) = ( $1, $2 )
  730. if $line =~ /^(\d+)\s+(\d+)$/;
  731. push( @$keep_list, $failure_file );
  732. } ## end if ( -f $failure_file )
  733. die "Cannot stat ${main::current_incoming_short}/$changes (??): $!\n"
  734. if !( @changes_stats = stat($changes) );
  735. # Make $upload_time the maximum of all modification times of files
  736. # related to this .changes (and the .changes it self). This is the
  737. # last time something changes to these files.
  738. $upload_time = $changes_stats[ST_MTIME];
  739. for $file (@files) {
  740. my @stats;
  741. next if !( @stats = stat( $file->{"name"} ) );
  742. $file->{"stats"} = \@stats;
  743. $upload_time = $stats[ST_MTIME] if $stats[ST_MTIME] > $upload_time;
  744. } ## end for $file (@files)
  745. $do_report = ( time - $upload_time ) > $conf::problem_report_timeout;
  746. # now check all files for correct size and md5 sum
  747. for $file (@files) {
  748. my $filename = $file->{"name"};
  749. if ( !defined( $file->{"stats"} ) ) {
  750. # could be an upload that isn't complete yet, be quiet,
  751. # but don't process the file;
  752. msg( "log", "$filename doesn't exist (ignored for now)\n" );
  753. ++$errs;
  754. } elsif ( $file->{"stats"}->[ST_SIZE] < $file->{"size"}
  755. && !$do_report )
  756. {
  757. # could be an upload that isn't complete yet, be quiet,
  758. # but don't process the file
  759. msg( "log", "$filename is too small (ignored for now)\n" );
  760. ++$errs;
  761. } elsif ( $file->{"stats"}->[ST_SIZE] != $file->{"size"} ) {
  762. msg( "log,mail", "$filename has incorrect size; deleting it\n" );
  763. rm($filename);
  764. ++$errs;
  765. } elsif ( md5sum($filename) ne $file->{"md5"} ) {
  766. msg( "log,mail",
  767. "$filename has incorrect md5 checksum; ",
  768. "deleting it\n" );
  769. rm($filename);
  770. ++$errs;
  771. } ## end elsif ( md5sum($filename)...
  772. } ## end for $file (@files)
  773. if ($errs) {
  774. if ( ( time - $upload_time ) > $conf::bad_changes_timeout ) {
  775. # if a .changes fails for a really long time (several days
  776. # or so), remove it and all associated files
  777. msg(
  778. "log,mail",
  779. "$main::current_incoming_short/$changes couldn't be processed for ",
  780. int( $conf::bad_changes_timeout / ( 60 * 60 ) ),
  781. " hours and is now deleted\n"
  782. );
  783. msg( "log,mail", "All files it mentions are also removed:\n" );
  784. msg( "log,mail", " ", join( ", ", @filenames ), "\n" );
  785. rm( $changes, @filenames, $failure_file );
  786. }
  787. # else: be quiet
  788. return;
  789. } ## end if ($errs)
  790. # if this upload already failed earlier, wait until the delay requirement
  791. # is fulfilled
  792. if ( $retries > 0
  793. && ( time - $last_retry ) <
  794. ( $retries == 1 ? $conf::upload_delay_1 : $conf::upload_delay_2 ) )
  795. {
  796. msg( "log", "delaying retry of upload\n" );
  797. return;
  798. } ## end if ( $retries > 0 && (...
  799. return if !ftp_open();
  800. # check if the job is already present on target
  801. # (moved to here, to avoid bothering target as long as there are errors in
  802. # the job)
  803. if ( $ls_l = is_on_target( $changes, @filenames ) ) {
  804. msg(
  805. "log,mail",
  806. "$main::current_incoming_short/$changes is already present on target host:\n"
  807. );
  808. msg( "log,mail", "$ls_l\n" );
  809. msg( "mail",
  810. "Either you already uploaded it, or someone else ",
  811. "came first.\n" );
  812. msg( "log,mail", "Job $changes removed.\n" );
  813. rm( $changes, @filenames, $failure_file );
  814. return;
  815. } ## end if ( $ls_l = is_on_target...
  816. # clear sgid bit before upload, scp would copy it to target. We don't need
  817. # it anymore, we know there are no problems if we come here. Also change
  818. # mode of files to 644 if this should be done locally.
  819. $changes_stats[ST_MODE] &= ~S_ISGID;
  820. if ( !$conf::chmod_on_target ) {
  821. $changes_stats[ST_MODE] &= ~0777;
  822. $changes_stats[ST_MODE] |= 0644;
  823. }
  824. chmod +( $changes_stats[ST_MODE] ), $changes;
  825. # try uploading to target
  826. if ( !copy_to_target( $changes, @filenames ) ) {
  827. # if the upload failed, increment the retry counter and remember the
  828. # current time; both things are written to the .failures file. Don't
  829. # increment the fail counter if the error was due to incoming
  830. # unwritable.
  831. return if !$main::incoming_writable;
  832. if ( ++$retries >= $conf::max_upload_retries ) {
  833. msg( "log,mail",
  834. "$changes couldn't be uploaded for $retries times now.\n" );
  835. msg( "log,mail",
  836. "Giving up and removing it and its associated files:\n" );
  837. msg( "log,mail", " ", join( ", ", @filenames ), "\n" );
  838. rm( $changes, @filenames, $failure_file );
  839. } else {
  840. $last_retry = time;
  841. if ( open( FAILS, ">", $failure_file ) ) {
  842. print FAILS "$retries $last_retry\n";
  843. close(FAILS);
  844. chmod( 0600, $failure_file )
  845. or die "Cannot set modes of $failure_file: $!\n";
  846. } ## end if ( open( FAILS, ">$failure_file"...
  847. push( @$keep_list, $failure_file );
  848. debug("now $retries failed uploads");
  849. msg(
  850. "mail",
  851. "The upload will be retried in ",
  852. print_time(
  853. $retries == 1
  854. ? $conf::upload_delay_1
  855. : $conf::upload_delay_2
  856. ),
  857. "\n"
  858. );
  859. } ## end else [ if ( ++$retries >= $conf::max_upload_retries)
  860. return;
  861. } ## end if ( !copy_to_target( ...
  862. # If the files were uploaded ok, remove them
  863. rm( $changes, @filenames, $failure_file );
  864. msg( "mail", "$changes uploaded successfully to $conf::target\n" );
  865. msg( "mail", "along with the files:\n ", join( "\n ", @filenames ),
  866. "\n" );
  867. msg( "log",
  868. "$changes processed successfully (uploader $main::mail_addr)\n" );
  869. return;
  870. remove_only_changes:
  871. msg(
  872. "log,mail",
  873. "Removing $main::current_incoming_short/$changes, but keeping its "
  874. . "associated files for now.\n"
  875. );
  876. rm($changes);
  877. return;
  878. # Check for files that have the same stem as the .changes (and weren't
  879. # mentioned there) and delete them. It happens often enough that people
  880. # upload a .orig.tar.gz where it isn't needed and also not in the
  881. # .changes. Explicitly deleting it (and not waiting for the
  882. # $stray_remove_timeout) reduces clutter in the queue dir and maybe also
  883. # educates uploaders :-)
  884. # my $pattern = debian_file_stem( $changes );
  885. # my $spattern = substr( $pattern, 0, -1 ); # strip off '*' at end
  886. # my @other_files = glob($pattern);
  887. # filter out files that have a Debian revision at all and a different
  888. # revision. Those belong to a different upload.
  889. # if ($changes =~ /^\Q$spattern\E-([\d.+-]+)/) {
  890. # my $this_rev = $1;
  891. # @other_files = grep( !/^\Q$spattern\E-([\d.+-]+)/ || $1 eq $this_rev,
  892. # @other_files);
  893. #}
  894. # Also do not remove those files if a .changes is among them. Then there
  895. # is probably a second upload for another version or another architecture.
  896. # if (@other_files && !grep( /\.changes$/, @other_files )) {
  897. # rm( @other_files );
  898. # msg( "mail", "\nThe following file(s) seemed to belong to the same ".
  899. # "upload, but weren't listed\n" );
  900. # msg( "mail", "in the .changes file:\n " );
  901. # msg( "mail", join( "\n ", @other_files ), "\n" );
  902. # msg( "mail", "They have been deleted.\n" );
  903. # msg( "log", "Deleted files in upload not in $changes: @other_files\n" );
  904. #}
  905. } ## end sub process_changes($\@)
  906. #
  907. # process one .dak-commands file
  908. #
  909. sub process_dak_commands {
  910. my $commands = shift;
  911. msg("log", "processing ${main::current_incoming_short}/$commands\n");
  912. # TODO: get mail address from signed contents
  913. # and NOT implement a third parser for armored PGP...
  914. $main::mail_addr = undef;
  915. # check signature
  916. my $signator = pgp_check($commands);
  917. if (!$signator) {
  918. msg("log,mail",
  919. "$main::current_incoming_short/$commands has bad PGP/GnuPG signature!\n");
  920. msg("log,mail",
  921. "Removing $main::current_incoming_short/$commands\n");
  922. rm($commands);
  923. return;
  924. }
  925. elsif ($signator eq 'LOCAL ERROR') {
  926. debug("Can't check signature for $main::current_incoming_short/$commands -- don't process it for now");
  927. return;
  928. }
  929. msg("log,mail", "(PGP/GnuPG signature by $signator)\n");
  930. return if !ftp_open();
  931. # check target
  932. my @filenames = ($commands);
  933. if (my $ls_l = is_on_target($commands, @filenames)) {
  934. msg("log,mail", "$main::current_incoming_short/$commands is already present on target host:\n");
  935. msg("log,mail", "$ls_l\n");
  936. msg("log,mail", "Job $commands removed.\n");
  937. rm($commands);
  938. return;
  939. }
  940. if (!copy_to_target($commands)) {
  941. msg("log,mail", "$commands couldn't be uploaded to target.\n");
  942. msg("log,mail", "Giving up and removing it.\n");
  943. rm($commands);
  944. return;
  945. }
  946. rm($commands);
  947. msg("mail", "$commands uploaded successfully to $conf::target\n");
  948. }
  949. #
  950. # process one .commands file
  951. #
  952. sub process_commands($) {
  953. my $commands = shift;
  954. my ( @cmds, $cmd, $pgplines, $signator );
  955. local (*COMMANDS);
  956. my ($file, @removed, $target_delay );
  957. format_status_str( $main::current_changes, $commands );
  958. $main::dstat = "c";
  959. $main::mail_addr = "";
  960. write_status_file() if $conf::statusdelay;
  961. msg( "log", "processing $main::current_incoming_short/$commands\n" );
  962. # run PGP on the file to check the signature
  963. if ( !( $signator = pgp_check($commands) ) ) {
  964. msg(
  965. "log,mail",
  966. "$main::current_incoming_short/$commands has bad PGP/GnuPG signature!\n"
  967. );
  968. goto remove;
  969. } elsif ( $signator eq "LOCAL ERROR" ) {
  970. # An error has appened when starting pgp... Don't process the file,
  971. # but also don't delete it
  972. debug(
  973. "Can't PGP/GnuPG check $main::current_incoming_short/$commands -- don't process it for now"
  974. );
  975. return;
  976. } ## end elsif ( $signator eq "LOCAL ERROR")
  977. msg( "log", "(PGP/GnuPG signature by $signator)\n" );
  978. # parse the .commands file
  979. if ( !open( COMMANDS, "<", $commands ) ) {
  980. msg( "log", "Cannot open $main::current_incoming_short/$commands: $!\n" );
  981. return;
  982. }
  983. $pgplines = 0;
  984. @cmds = ();
  985. outer_loop: while (<COMMANDS>) {
  986. if (/^---+(BEGIN|END) PGP .*---+$/) {
  987. ++$pgplines;
  988. next;
  989. }
  990. if ($pgplines != 1) {
  991. next;
  992. }
  993. if (/^Uploader:\s*/i) {
  994. chomp( $main::mail_addr = $' );
  995. $main::mail_addr = $1 if $main::mail_addr =~ /<([^>]*)>/;
  996. } elsif (/^Commands:/i) {
  997. $_ = $';
  998. for ( ; ; ) {
  999. s/^\s*(.*)\s*$/$1/; # delete whitespace at both ends
  1000. if ( !/^\s*$/ ) {
  1001. push( @cmds, $_ );
  1002. debug("includes cmd $_");
  1003. }
  1004. last outer_loop if !defined( $_ = scalar(<COMMANDS>) );
  1005. chomp;
  1006. redo outer_loop if !/^\s/ || /^$/;
  1007. } ## end for ( ; ; )
  1008. } ## end elsif (/^Commands:/i)
  1009. } ## end while (<COMMANDS>)
  1010. close(COMMANDS);
  1011. # some consistency checks
  1012. if ( !$main::mail_addr || $main::mail_addr !~ /^\S+\@\S+\.\S+/ ) {
  1013. msg( "log,mail",
  1014. "$main::current_incoming_short/$commands contains no or bad Uploader: field: "
  1015. . "$main::mail_addr\n" );
  1016. msg( "log,mail",
  1017. "cannot process $main::current_incoming_short/$commands\n" );
  1018. $main::mail_addr = "";
  1019. goto remove;
  1020. } ## end if ( !$main::mail_addr...
  1021. msg( "log", "(command uploader $main::mail_addr)\n" );
  1022. if ( $pgplines < 3 ) {
  1023. msg(
  1024. "log,mail",
  1025. "$main::current_incoming_short/$commands isn't signed with PGP/GnuPG\n"
  1026. );
  1027. msg(
  1028. "mail",
  1029. "or the uploaded file is broken. Make sure to transfer in binary mode\n"
  1030. );
  1031. msg( "mail", "or better yet - use dcut for commands files\n" );
  1032. goto remove;
  1033. } ## end if ( $pgplines < 3 )
  1034. # now process commands
  1035. msg(
  1036. "mail",
  1037. "Log of processing your commands file $main::current_incoming_short/$commands:\n\n"
  1038. );
  1039. foreach $cmd (@cmds) {
  1040. my @word = split( /\s+/, $cmd );
  1041. msg( "mail,log", "> @word\n" );
  1042. my $selecteddelayed = -1;
  1043. next if @word < 1;
  1044. if ( $word[0] eq "rm" ) {
  1045. my @files = ();
  1046. foreach ( @word[ 1 .. $#word ] ) {
  1047. my $origword = $_;
  1048. if (m,^DELAYED/([0-9]+)-day/,) {
  1049. $selecteddelayed = $1;
  1050. s,^DELAYED/[0-9]+-day/,,;
  1051. }
  1052. if (m,(^|/)\*,) {
  1053. msg("mail,log", "$_: filename component cannot start with a wildcard\n");
  1054. } elsif ( $origword eq "--searchdirs" ) {
  1055. $selecteddelayed = -2;
  1056. } elsif (m,/,) {
  1057. msg(
  1058. "mail,log",
  1059. "$_: filename may not contain slashes except for DELAYED/#-day/ prefixes\n"
  1060. );
  1061. } else {
  1062. # process wildcards but also plain names
  1063. my (@thesefiles);
  1064. my $pat = quotemeta($_);
  1065. $pat =~ s/\\\*/.*/g;
  1066. $pat =~ s/\\\?/.?/g;
  1067. $pat =~ s/\\([][])/$1/g;
  1068. if ( $selecteddelayed < 0 ) { # scanning or explicitly incoming
  1069. opendir( DIR, "." );
  1070. push( @thesefiles, grep /^$pat$/, readdir(DIR) );
  1071. closedir(DIR);
  1072. }
  1073. if ( $selecteddelayed >= 0 ) {
  1074. my $dir = sprintf( $conf::incoming_delayed, $selecteddelayed );
  1075. opendir( DIR, $dir );
  1076. push( @thesefiles,
  1077. map ( "$dir/$_", grep /^$pat$/, readdir(DIR) ) );
  1078. closedir(DIR);
  1079. } elsif ( $selecteddelayed == -2 ) {
  1080. for ( my ($adelay) = 0 ;
  1081. ( !@thesefiles ) && $adelay <= $conf::max_delayed ;
  1082. $adelay++ )
  1083. {
  1084. my $dir = sprintf( $conf::incoming_delayed, $adelay );
  1085. opendir( DIR, $dir );
  1086. push( @thesefiles,
  1087. map ( "$dir/$_", grep /^$pat$/, readdir(DIR) ) );
  1088. closedir(DIR);
  1089. } ## end for ( my ($adelay) = 0 ...
  1090. } ## end elsif ( $selecteddelayed ...
  1091. push( @files, @thesefiles );
  1092. if ( !@thesefiles ) {
  1093. msg( "mail,log", "$origword did not match anything\n" );
  1094. }
  1095. } ## end else [ if ( $origword eq "--searchdirs")
  1096. } ## end foreach ( @word[ 1 .. $#word...
  1097. if ( !@files ) {
  1098. msg( "mail,log", "No files to delete\n" );
  1099. } else {
  1100. @removed = ();
  1101. foreach $file (@files) {
  1102. if ( !-f $file ) {
  1103. msg( "mail,log", "$file: no such file\n" );
  1104. } elsif ( $file =~ /$conf::keep_files/ ) {
  1105. msg( "mail,log", "$file is protected, cannot " . "remove\n" );
  1106. } elsif ( !unlink($file) ) {
  1107. msg( "mail,log", "$file: rm: $!\n" );
  1108. } else {
  1109. $file =~ s,$conf::incoming/?,,;
  1110. push( @removed, $file );
  1111. }
  1112. } ## end foreach $file (@files)
  1113. msg( "mail,log", "Files removed: @removed\n" ) if @removed;
  1114. } ## end else [ if ( !@files )
  1115. } elsif ( $word[0] eq "reschedule" ) {
  1116. if ( @word != 3 ) {
  1117. msg( "mail,log", "Wrong number of arguments\n" );
  1118. } elsif ( $conf::upload_method ne "copy" ) {
  1119. msg( "mail,log", "reschedule not available\n" );
  1120. } elsif ( $word[1] =~ m,/, || $word[1] !~ m/\.changes/ ) {
  1121. msg(
  1122. "mail,log",
  1123. "$word[1]: filename may not contain slashes and must be .changes\n"
  1124. );
  1125. } elsif ( !( ($target_delay) = $word[2] =~ m,^([0-9]+)-day$, )
  1126. || $target_delay > $conf::max_delayed )
  1127. {
  1128. msg(
  1129. "mail,log",
  1130. "$word[2]: rescheduling target must be #-day with # between 0 and $conf::max_delayed (in particular, no '/' allowed)\n"
  1131. );
  1132. } elsif ( $word[1] =~ /$conf::keep_files/ ) {
  1133. msg( "mail,log", "$word[1] is protected, cannot do stuff with it\n" );
  1134. } else {
  1135. my ($adelay);
  1136. for ( $adelay = 0 ;
  1137. $adelay <= $conf::max_delayed
  1138. && !-f (
  1139. sprintf( "$conf::targetdir_delayed", $adelay ) . "/$word[1]" ) ;
  1140. $adelay++ )
  1141. {
  1142. } ## end for ( $adelay = 0 ; $adelay...
  1143. if ( $adelay > $conf::max_delayed ) {
  1144. msg( "mail,log", "$word[1] not found\n" );
  1145. } elsif ( $adelay == $target_delay ) {
  1146. msg( "mail,log", "$word[1] already is in $word[2]\n" );
  1147. } else {
  1148. my (@thesefiles);
  1149. my ($dir) = sprintf( "$conf::targetdir_delayed", $adelay );
  1150. my ($target_dir) =
  1151. sprintf( "$conf::targetdir_delayed", $target_delay );
  1152. push( @thesefiles, $word[1] );
  1153. push( @thesefiles,
  1154. get_filelist_from_known_good_changes("$dir/$word[1]") );
  1155. for my $afile (@thesefiles) {
  1156. if ( $afile =~ m/\.changes$/ ) {
  1157. utime undef, undef, ("$dir/$afile");
  1158. }
  1159. if ( !move("$dir/$afile", "$target_dir/$afile") ) {
  1160. msg( "mail,log", "move: $!\n" );
  1161. } else {
  1162. msg( "mail,log", "$afile moved to $target_delay-day\n" );
  1163. }
  1164. } ## end for my $afile (@thesefiles)
  1165. } ## end else [ if ( $adelay > $conf::max_delayed)
  1166. } ## end else [ if ( @word != 3 )
  1167. } elsif ( $word[0] eq "cancel" ) {
  1168. if ( @word != 2 ) {
  1169. msg( "mail,log", "Wrong number of arguments\n" );
  1170. } elsif ( $conf::upload_method ne "copy" ) {
  1171. msg( "mail,log", "cancel not available\n" );
  1172. } elsif (
  1173. $word[1] !~ m,$re_file_safe_prefix\.changes\z, )
  1174. {
  1175. msg( "mail,log",
  1176. "argument to cancel must be one .changes filename without path\n" );
  1177. } ## end elsif ( $word[1] !~ ...
  1178. my (@files) = ();
  1179. for ( my ($adelay) = 0 ; $adelay <= $conf::max_delayed ; $adelay++ ) {
  1180. my ($dir) = sprintf( "$conf::targetdir_delayed", $adelay );
  1181. if ( -f "$dir/$word[1]" ) {
  1182. @removed = ();
  1183. push( @files, "$word[1]" );
  1184. push( @files,
  1185. get_filelist_from_known_good_changes("$dir/$word[1]") );
  1186. foreach $file (@files) {
  1187. if ( !-f "$dir/$file" ) {
  1188. msg( "mail,log", "$dir/$file: no such file\n" );
  1189. } elsif ( "$dir/$file" =~ /$conf::keep_files/ ) {
  1190. msg( "mail,log",
  1191. "$dir/$file is protected, cannot " . "remove\n" );
  1192. } elsif ( !unlink("$dir/$file") ) {
  1193. msg( "mail,log", "$dir/$file: rm: $!\n" );
  1194. } else {
  1195. push( @removed, $file );
  1196. }
  1197. } ## end foreach $file (@files)
  1198. msg( "mail,log", "Files removed from $adelay-day: @removed\n" )
  1199. if @removed;
  1200. } ## end if ( -f "$dir/$word[1]")
  1201. } ## end for ( my ($adelay) = 0 ...
  1202. if ( !@files ) {
  1203. msg( "mail,log", "No upload found: $word[1]\n" );
  1204. }
  1205. } else {
  1206. msg( "mail,log", "unknown command $word[0]\n" );
  1207. }
  1208. } ## end foreach $cmd (@cmds)
  1209. rm($commands);
  1210. msg( "log",
  1211. "-- End of $main::current_incoming_short/$commands processing\n" );
  1212. return;
  1213. remove:
  1214. msg("log,mail", "Removing $main::current_incoming_short/$commands\n");
  1215. rm($commands);
  1216. return;
  1217. } ## end sub process_commands($)
  1218. sub age_delayed_queues() {
  1219. for ( my ($adelay) = 0 ; $adelay <= $conf::max_delayed ; $adelay++ ) {
  1220. my ($dir) = sprintf( "$conf::targetdir_delayed", $adelay );
  1221. my ($target_dir);
  1222. if ( $adelay == 0 ) {
  1223. $target_dir = $conf::targetdir;
  1224. } else {
  1225. $target_dir = sprintf( "$conf::targetdir_delayed", $adelay - 1 );
  1226. }
  1227. for my $achanges (<$dir/*.changes>) {
  1228. my $mtime = ( stat($achanges) )[9];
  1229. if ( $mtime + 24 * 60 * 60 <= time || $adelay == 0 ) {
  1230. utime undef, undef, ($achanges);
  1231. my @thesefiles = ( $achanges =~ m,.*/([^/]*), );
  1232. push( @thesefiles, get_filelist_from_known_good_changes($achanges) );
  1233. for my $afile (@thesefiles) {
  1234. if ( !move("$dir/$afile", "$target_dir/$afile") ) {
  1235. msg( "log", "move: $!\n" );
  1236. } else {
  1237. msg( "log", "$afile moved to $target_dir\n" );
  1238. }
  1239. } ## end for my $afile (@thesefiles)
  1240. } ## end if ( $mtime + 24 * 60 ...
  1241. } ## end for my $achanges (<$dir/*.changes>)
  1242. } ## end for ( my ($adelay) = 0 ...
  1243. } ## end sub age_delayed_queues()
  1244. #
  1245. # check if a file is already on target
  1246. #
  1247. sub is_on_target($\@) {
  1248. my $file = shift;
  1249. my $filelist = shift;
  1250. my $msg;
  1251. my $stat;
  1252. if ( $conf::upload_method eq "ssh" ) {
  1253. ( $msg, $stat ) = ssh_cmd("ls -l $file");
  1254. } elsif ( $conf::upload_method eq "ftp" ) {
  1255. my $err;
  1256. ( $msg, $err ) = ftp_cmd( "dir", $file );
  1257. if ($err) {
  1258. $stat = 1;
  1259. $msg = $err;
  1260. } elsif ( !$msg ) {
  1261. $stat = 1;
  1262. $msg = "ls: no such file\n";
  1263. } else {
  1264. $stat = 0;
  1265. $msg = join( "\n", @$msg );
  1266. }
  1267. } else {
  1268. my @allfiles = ($file);
  1269. push( @allfiles, @$filelist );
  1270. $stat = 1;
  1271. $msg = "no such file";
  1272. for my $afile (@allfiles) {
  1273. if ( -f "$conf::targetdir/$afile" ) {
  1274. $stat = 0;
  1275. $msg = "$afile";
  1276. }
  1277. } ## end for my $afile (@allfiles)
  1278. for ( my ($adelay) = 0 ;
  1279. $adelay <= $conf::max_delayed && $stat ;
  1280. $adelay++ )
  1281. {
  1282. for my $afile (@allfiles) {
  1283. if (
  1284. -f ( sprintf( "$conf::targetdir_delayed", $adelay ) . "/$afile" ) )
  1285. {
  1286. $stat = 0;
  1287. $msg = sprintf( "%d-day", $adelay ) . "/$afile";
  1288. } ## end if ( -f ( sprintf( "$conf::targetdir_delayed"...
  1289. } ## end for my $afile (@allfiles)
  1290. } ## end for ( my ($adelay) = 0 ...
  1291. } ## end else [ if ( $conf::upload_method...
  1292. chomp($msg);
  1293. debug("exit status: $stat, output was: $msg");
  1294. return "" if $stat && $msg =~ /no such file/i; # file not present
  1295. msg( "log", "strange ls -l output on target:\n", $msg ), return ""
  1296. if $stat || $@; # some other error, but still try to upload
  1297. # ls -l returned 0 -> file already there
  1298. $msg =~ s/\s\s+/ /g; # make multiple spaces into one, to save space
  1299. return $msg;
  1300. } ## end sub is_on_target($\@)
  1301. #
  1302. # copy a list of files to target
  1303. #
  1304. sub copy_to_target(@) {
  1305. my @files = @_;
  1306. my ( @md5sum, @expected_files, $sum, $name, $msgs, $stat );
  1307. $main::dstat = "u";
  1308. write_status_file() if $conf::statusdelay;
  1309. # copy the files
  1310. if ( $conf::upload_method eq "ssh" ) {
  1311. ( $msgs, $stat ) = scp_cmd(@files);
  1312. goto err if $stat;
  1313. } elsif ( $conf::upload_method eq "ftp" ) {
  1314. my ( $rv, $file );
  1315. if ( !$main::FTP_chan->cwd($main::current_targetdir) ) {
  1316. msg( "log,mail",
  1317. "Can't cd to $main::current_targetdir on $conf::target\n" );
  1318. goto err;
  1319. }
  1320. foreach $file (@files) {
  1321. ( $rv, $msgs ) = ftp_cmd( "put", $file );
  1322. goto err if !$rv;
  1323. }
  1324. } else {
  1325. for my $file (@files) {
  1326. eval { File::Copy::copy($file, $main::current_targetdir) };
  1327. if ($@) {
  1328. $stat = 1;
  1329. $msgs = $@;
  1330. goto err;
  1331. }
  1332. }
  1333. }
  1334. # check md5sums or sizes on target against our own
  1335. my $have_md5sums = 1;
  1336. if ($conf::check_md5sum) {
  1337. if ( $conf::upload_method eq "ssh" ) {
  1338. ( $msgs, $stat ) = ssh_cmd("md5sum @files");
  1339. goto err if $stat;
  1340. @md5sum = split( "\n", $msgs );
  1341. } elsif ( $conf::upload_method eq "ftp" ) {
  1342. my ( $rv, $err, $file );
  1343. foreach $file (@files) {
  1344. ( $rv, $err ) = ftp_cmd( "quot", "site", "md5sum", $file );
  1345. if ($err) {
  1346. next if ftp_code() == 550; # file not found
  1347. if ( ftp_code() == 500 ) { # unimplemented
  1348. $have_md5sums = 0;
  1349. goto get_sizes_instead;
  1350. }
  1351. $msgs = $err;
  1352. goto err;
  1353. } ## end if ($err)
  1354. chomp( my $t = ftp_response() );
  1355. push( @md5sum, $t );
  1356. } ## end foreach $file (@files)
  1357. if ( !$have_md5sums ) {
  1358. get_sizes_instead:
  1359. foreach $file (@files) {
  1360. ( $rv, $err ) = ftp_cmd( "size", $file );
  1361. if ($err) {
  1362. next if ftp_code() == 550; # file not found
  1363. $msgs = $err;
  1364. goto err;
  1365. }
  1366. push( @md5sum, "$rv $file" );
  1367. } ## end foreach $file (@files)
  1368. } ## end if ( !$have_md5sums )
  1369. } else {
  1370. for my $file (@files) {
  1371. my $md5 = eval { md5sum("$main::current_targetdir/$file") };
  1372. if ($@) {
  1373. $msgs = $@;
  1374. goto err;
  1375. }
  1376. push @md5sum, "$md5 $file" if $md5;
  1377. }
  1378. }
  1379. @expected_files = @files;
  1380. foreach (@md5sum) {
  1381. chomp;
  1382. ( $sum, $name ) = split;
  1383. next if !grep { $_ eq $name } @files; # a file we didn't upload??
  1384. next if $sum eq "md5sum:"; # looks like an error message
  1385. if ( ( $have_md5sums && $sum ne md5sum($name) )
  1386. || ( !$have_md5sums && $sum != ( -s $name ) ) )
  1387. {
  1388. msg(
  1389. "log,mail",
  1390. "Upload of $name to $conf::target failed ",
  1391. "(" . ( $have_md5sums ? "md5sum" : "size" ) . " mismatch)\n"
  1392. );
  1393. goto err;
  1394. } ## end if ( ( $have_md5sums &&...
  1395. # seen that file, remove it from expect list
  1396. @expected_files = map { $_ eq $name ? () : $_ } @expected_files;
  1397. } ## end foreach (@md5sum)
  1398. if (@expected_files) {
  1399. msg( "log,mail", "Failed to upload the files\n" );
  1400. msg( "log,mail", " ", join( ", ", @expected_files ), "\n" );
  1401. msg( "log,mail", "(Not present on target after upload)\n" );
  1402. goto err;
  1403. } ## end if (@expected_files)
  1404. } ## end if ($conf::check_md5sum)
  1405. if ($conf::chmod_on_target) {
  1406. # change file's mode explicitly to 644 on target
  1407. if ( $conf::upload_method eq "ssh" ) {
  1408. ( $msgs, $stat ) = ssh_cmd("chmod 644 @files");
  1409. goto err if $stat;
  1410. } elsif ( $conf::upload_method eq "ftp" ) {
  1411. my ( $rv, $file );
  1412. foreach $file (@files) {
  1413. ( $rv, $msgs ) = ftp_cmd( "quot", "site", "chmod", "644", $file );
  1414. msg( "log", "Can't chmod $file on target:\n$msgs" )
  1415. if $msgs;
  1416. goto err if !$rv;
  1417. } ## end foreach $file (@files)
  1418. } else {
  1419. for my $file (@files) {
  1420. unless (chmod 0644, "$main::current_targetdir/$file") {
  1421. $msgs = "Could not chmod $file: $!";
  1422. goto err;
  1423. }
  1424. }
  1425. }
  1426. } ## end if ($conf::chmod_on_target)
  1427. $main::dstat = "c";
  1428. write_status_file() if $conf::statusdelay;
  1429. return 1;
  1430. err:
  1431. msg( "log,mail",
  1432. "Upload to $conf::target failed",
  1433. $? ? ", last exit status " . sprintf( "%s", $? >> 8 ) : "", "\n" );
  1434. msg( "log,mail", "Error messages:\n", $msgs )
  1435. if $msgs;
  1436. # If "permission denied" was among the errors, test if the incoming is
  1437. # writable at all.
  1438. if ( $msgs && $msgs =~ /(permission denied|read-?only file)/i ) {
  1439. if ( !check_incoming_writable() ) {
  1440. msg( "log,mail", "(The incoming directory seems to be ",
  1441. "unwritable.)\n" );
  1442. }
  1443. } ## end if ( $msgs =~ /(permission denied|read-?only file)/i)
  1444. # remove bad files or an incomplete upload on target
  1445. if ( $conf::upload_method eq "ssh" ) {
  1446. ssh_cmd("rm -f @files");
  1447. } elsif ( $conf::upload_method eq "ftp" ) {
  1448. my $file;
  1449. foreach $file (@files) {
  1450. my ( $rv, $err );
  1451. ( $rv, $err ) = ftp_cmd( "delete", $file );
  1452. msg( "log", "Can't delete $file on target:\n$err" )
  1453. if $err;
  1454. } ## end foreach $file (@files)
  1455. } else {
  1456. my @tfiles = map { "$main::current_targetdir/$_" } @files;
  1457. debug("executing unlink(@tfiles)");
  1458. rm(@tfiles);
  1459. }
  1460. $main::dstat = "c";
  1461. write_status_file() if $conf::statusdelay;
  1462. return 0;
  1463. } ## end sub copy_to_target(@)
  1464. #
  1465. # check if a file is correctly signed with PGP
  1466. #
  1467. sub pgp_check($) {
  1468. my $file = shift;
  1469. my $output = "";
  1470. my $signator;
  1471. my $found = 0;
  1472. my $stat = 1;
  1473. local (*PIPE);
  1474. local $_;
  1475. if ($file =~ /$re_file_safe/) {
  1476. $file = $1;
  1477. } else {
  1478. msg( "log", "Tainted filename, skipping: $file\n" );
  1479. return "LOCAL ERROR";
  1480. }
  1481. # check the file has only one clear-signed section
  1482. my $fh;
  1483. unless (open $fh, "<", $file) {
  1484. msg("log,mail", "Could not open $file\n");
  1485. return "";
  1486. }
  1487. unless (<$fh> eq "-----BEGIN PGP SIGNED MESSAGE-----\n") {
  1488. msg("log,mail", "$file: does not start with a clearsigned message\n");
  1489. return "";
  1490. }
  1491. my $pgplines = 1;
  1492. while (<$fh>) {
  1493. if (/\A- /) {
  1494. msg("log,mail", "$file: dash-escaped messages are not accepted\n");
  1495. return "";
  1496. }
  1497. elsif ($_ eq "-----BEGIN PGP SIGNATURE-----\n"
  1498. || $_ eq "-----END PGP SIGNATURE-----\n") {
  1499. $pgplines++;
  1500. }
  1501. elsif (/\A--/) {
  1502. msg("log,mail", "$file: unexpected OpenPGP armor\n");
  1503. return "";
  1504. }
  1505. elsif ($pgplines > 3 && /\S/) {
  1506. msg("log,mail", "$file: found text after end of signature\n");
  1507. return "";
  1508. }
  1509. }
  1510. if ($pgplines != 3) {
  1511. msg("log,mail", "$file: doesn't seem to be a valid clearsigned OpenPGP message\n");
  1512. return "";
  1513. }
  1514. close $fh;
  1515. if ( -x $conf::gpg ) {
  1516. my @command = ("$conf::gpg", "--no-options", "--batch", "--no-tty",
  1517. "--trust-model", "always", "--no-default-keyring",
  1518. (map +("--keyring" => $_), @conf::keyrings),
  1519. "--verify-options", "no-show-uid-validity",
  1520. "--verify", "-");
  1521. debug( "executing " . join(" ", @command) );
  1522. my $child = open(PIPE, "-|");
  1523. if (!defined($child)) {
  1524. msg("log", "Can't open pipe to $conf::gpg: $!\n");
  1525. return "LOCAL ERROR";
  1526. }
  1527. if ($child == 0) {
  1528. unless (open(STDERR, ">&", \*STDOUT)) {
  1529. print "Could not redirect STDERR.";
  1530. exit(-1);
  1531. }
  1532. unless (open(STDIN, "<", $file)) {
  1533. print "Could not open $file: $!";
  1534. exit(-1);
  1535. }
  1536. { exec(@command) }; # BLOCK avoids warning about likely unreachable code
  1537. print "Could not exec gpg: $!";
  1538. exit(-1);
  1539. }
  1540. $output .= $_ while (<PIPE>);
  1541. close(PIPE);
  1542. $stat = $?;
  1543. } ## end if ( -x $conf::gpg )
  1544. if ($stat) {
  1545. msg( "log,mail", "GnuPG signature check failed on $file\n" );
  1546. msg( "mail", $output );
  1547. msg( "log,mail", "(Exit status ", $stat >> 8, ")\n" );
  1548. return "";
  1549. } ## end if ($stat)
  1550. $output =~ /^(?:gpg: )?good signature from (?:user )?"(.*)"\.?$/im;
  1551. ( $signator = $1 ) ||= "unknown signator";
  1552. if ($conf::debug) {
  1553. debug("GnuPG signature ok (by $signator)");
  1554. }
  1555. return $signator;
  1556. } ## end sub pgp_check($)
  1557. # ---------------------------------------------------------------------------
  1558. # the status daemon
  1559. # ---------------------------------------------------------------------------
  1560. #
  1561. # fork a subprocess that watches the 'status' FIFO
  1562. #
  1563. # that process blocks until someone opens the FIFO, then sends a
  1564. # signal (SIGUSR1) to the main process, expects
  1565. #
  1566. sub fork_statusd() {
  1567. my $statusd_pid;
  1568. my $main_pid = $$;
  1569. my $errs;
  1570. local (*STATFIFO);
  1571. $statusd_pid = open( STATUSD, "|-" );
  1572. die "cannot fork: $!\n" if !defined($statusd_pid);
  1573. # parent just returns
  1574. if ($statusd_pid) {
  1575. msg( "log", "forked status daemon (pid $statusd_pid)\n" );
  1576. return $statusd_pid;
  1577. }
  1578. # child: the status FIFO daemon
  1579. # ignore SIGPIPE here, in case some closes the FIFO without completely
  1580. # reading it
  1581. $SIG{"PIPE"} = "IGNORE";
  1582. # also ignore SIGCLD, we don't want to inherit the restart-statusd handler
  1583. # from our parent
  1584. $SIG{"CHLD"} = "DEFAULT";
  1585. rm($conf::statusfile);
  1586. POSIX::mkfifo($conf::statusfile, 0644)
  1587. or die "Cannot create named pipe $conf::statusfile: $!\n";
  1588. chmod( 0644, $conf::statusfile )
  1589. or die "Cannot set modes of $conf::statusfile: $!\n";
  1590. # close log file, so that log rotating works
  1591. close(LOG);
  1592. close(STDOUT);
  1593. close(STDERR);
  1594. while (1) {
  1595. my ( $status, $mup, $incw, $ds, $next_run, $last_ping, $currch, $l );
  1596. # open the FIFO for writing; this blocks until someone (probably ftpd)
  1597. # opens it for reading
  1598. open( STATFIFO, ">", $conf::statusfile )
  1599. or die "Cannot open $conf::statusfile\n";
  1600. select(STATFIFO);
  1601. # tell main daemon to send us status infos
  1602. kill( $main::signo{"USR1"}, $main_pid );
  1603. # get the infos from stdin; must loop until enough bytes received!
  1604. my $expect_len = 3 + 2 * STATNUM_LEN + STATSTR_LEN;
  1605. for ( $status = "" ; ( $l = length($status) ) < $expect_len ; ) {
  1606. sysread( STDIN, $status, $expect_len - $l, $l );
  1607. }
  1608. # disassemble the status byte stream
  1609. my $pos = 0;
  1610. foreach (
  1611. [ mup => 1 ],
  1612. [ incw => 1 ],
  1613. [ ds => 1 ],
  1614. [ next_run => STATNUM_LEN ],
  1615. [ last_ping => STATNUM_LEN ],
  1616. [ currch => STATSTR_LEN ]
  1617. )
  1618. {
  1619. eval "\$$_->[0] = substr( \$status, $pos, $_->[1] );";
  1620. $pos += $_->[1];
  1621. } ## end foreach ( [ mup => 1 ], [ incw...
  1622. $currch =~ s/\n+//g;
  1623. print_status( $mup, $incw, $ds, $next_run, $last_ping, $currch );
  1624. close(STATFIFO);
  1625. # This sleep is necessary so that we can't reopen the FIFO
  1626. # immediately, in case the reader hasn't closed it yet if we get to
  1627. # the open again. Is there a better solution for this??
  1628. sleep 1;
  1629. } ## end while (1)
  1630. } ## end sub fork_statusd()
  1631. #
  1632. # update the status file, in case we use a plain file and not a FIFO
  1633. #
  1634. sub write_status_file() {
  1635. return if !$conf::statusfile;
  1636. open( STATFILE, ">", $conf::statusfile )
  1637. or ( msg( "log", "Could not open $conf::statusfile: $!\n" ), return );
  1638. my $oldsel = select(STATFILE);
  1639. print_status(
  1640. $main::target_up, $main::incoming_writable,
  1641. $main::dstat, $main::next_run,
  1642. $main::last_ping_time, $main::current_changes
  1643. );
  1644. select($oldsel);
  1645. close(STATFILE);
  1646. } ## end sub write_status_file()
  1647. sub print_status($$$$$$) {
  1648. my $mup = shift;
  1649. my $incw = shift;
  1650. my $ds = shift;
  1651. my $next_run = shift;
  1652. my $last_ping = shift;
  1653. my $currch = shift;
  1654. my $approx;
  1655. my $version;
  1656. ( $version = 'Release: 0.9 $Revision: 1.51 $' ) =~ s/\$ ?//g;
  1657. print "debianqueued $version\n";
  1658. $approx = $conf::statusdelay ? "approx. " : "";
  1659. if ( $mup eq "0" ) {
  1660. print "$conf::target is down, queue pausing\n";
  1661. return;
  1662. } elsif ( $conf::upload_method ne "copy" ) {
  1663. print "$conf::target seems to be up, last ping $approx",
  1664. print_time( time - $last_ping ), " ago\n";
  1665. }
  1666. if ( $incw eq "0" ) {
  1667. print "The incoming directory is not writable, queue pausing\n";
  1668. return;
  1669. }
  1670. if ( $ds eq "i" ) {
  1671. print "Next queue check in $approx", print_time( $next_run - time ), "\n";
  1672. return;
  1673. } elsif ( $ds eq "c" ) {
  1674. print "Checking queue directory\n";
  1675. } elsif ( $ds eq "u" ) {
  1676. print "Uploading to $conf::target\n";
  1677. } else {
  1678. print "Bad status data from daemon: \"$mup$incw$ds\"\n";
  1679. return;
  1680. }
  1681. print "Current job is $currch\n" if $currch;
  1682. } ## end sub print_status($$$$$$)
  1683. #
  1684. # format a number for sending to statusd (fixed length STATNUM_LEN)
  1685. #
  1686. sub format_status_num(\$$) {
  1687. my $varref = shift;
  1688. my $num = shift;
  1689. $$varref = sprintf "%" . STATNUM_LEN . "d", $num;
  1690. } ## end sub format_status_num(\$$)
  1691. #
  1692. # format a string for sending to statusd (fixed length STATSTR_LEN)
  1693. #
  1694. sub format_status_str(\$$) {
  1695. my $varref = shift;
  1696. my $str = shift;
  1697. $$varref = substr( $str, 0, STATSTR_LEN );
  1698. $$varref .= "\n" x ( STATSTR_LEN - length($$varref) );
  1699. } ## end sub format_status_str(\$$)
  1700. #
  1701. # send a status string to the status daemon
  1702. #
  1703. # Avoid all operations that could call malloc() here! Most libc
  1704. # implementations aren't reentrant, so we may not call it from a
  1705. # signal handler. So use only already-defined variables.
  1706. #
  1707. sub send_status() {
  1708. local $! = 0; # preserve errno
  1709. # re-setup handler, in case we have broken SysV signals
  1710. $SIG{"USR1"} = \&send_status;
  1711. syswrite( STATUSD, $main::target_up, 1 );
  1712. syswrite( STATUSD, $main::incoming_writable, 1 );
  1713. syswrite( STATUSD, $main::dstat, 1 );
  1714. syswrite( STATUSD, $main::next_run, STATNUM_LEN );
  1715. syswrite( STATUSD, $main::last_ping_time, STATNUM_LEN );
  1716. syswrite( STATUSD, $main::current_changes, STATSTR_LEN );
  1717. } ## end sub send_status()
  1718. # ---------------------------------------------------------------------------
  1719. # FTP functions
  1720. # ---------------------------------------------------------------------------
  1721. #
  1722. # open FTP connection to target host if not already open
  1723. #
  1724. sub ftp_open() {
  1725. return 1 unless $conf::upload_method eq "ftp";
  1726. if ($main::FTP_chan) {
  1727. # is already open, but might have timed out; test with a cwd
  1728. return $main::FTP_chan
  1729. if $main::FTP_chan->cwd($main::current_targetdir);
  1730. # cwd didn't work, channel is closed, try to reopen it
  1731. $main::FTP_chan = undef;
  1732. } ## end if ($main::FTP_chan)
  1733. if (
  1734. !(
  1735. $main::FTP_chan =
  1736. Net::FTP->new(
  1737. $conf::target,
  1738. Debug => $conf::ftpdebug,
  1739. Timeout => $conf::ftptimeout,
  1740. Passive => 1,
  1741. )
  1742. )
  1743. )
  1744. {
  1745. msg( "log,mail", "Cannot open FTP server $conf::target\n" );
  1746. goto err;
  1747. } ## end if ( !( $main::FTP_chan...
  1748. if ( !$main::FTP_chan->login() ) {
  1749. msg( "log,mail", "Anonymous login on FTP server $conf::target failed\n" );
  1750. goto err;
  1751. }
  1752. if ( !$main::FTP_chan->binary() ) {
  1753. msg( "log,mail", "Can't set binary FTP mode on $conf::target\n" );
  1754. goto err;
  1755. }
  1756. if ( !$main::FTP_chan->cwd($main::current_targetdir) ) {
  1757. msg( "log,mail",
  1758. "Can't cd to $main::current_targetdir on $conf::target\n" );
  1759. goto err;
  1760. }
  1761. debug("opened FTP channel to $conf::target");
  1762. return 1;
  1763. err:
  1764. $main::FTP_chan = undef;
  1765. return 0;
  1766. } ## end sub ftp_open()
  1767. sub ftp_cmd($@) {
  1768. my $cmd = shift;
  1769. my ( $rv, $err );
  1770. my $direct_resp_cmd = ( $cmd eq "quot" );
  1771. debug( "executing FTP::$cmd(" . join( ", ", @_ ) . ")" );
  1772. $SIG{"ALRM"} = sub { die "timeout in FTP::$cmd\n" };
  1773. alarm($conf::remote_timeout);
  1774. eval { $rv = $main::FTP_chan->$cmd(@_); };
  1775. alarm(0);
  1776. $err = "";
  1777. $rv = ( ftp_code() =~ /^2/ ) ? 1 : 0 if $direct_resp_cmd;
  1778. if ($@) {
  1779. $err = $@;
  1780. undef $rv;
  1781. } elsif ( !$rv ) {
  1782. $err = ftp_response();
  1783. }
  1784. return ( $rv, $err );
  1785. } ## end sub ftp_cmd($@)
  1786. sub ftp_close() {
  1787. if ($main::FTP_chan) {
  1788. $main::FTP_chan->quit();
  1789. $main::FTP_chan = undef;
  1790. }
  1791. return 1;
  1792. } ## end sub ftp_close()
  1793. sub ftp_response() {
  1794. return join( '', @{ ${*$main::FTP_chan}{'net_cmd_resp'} } );
  1795. }
  1796. sub ftp_code() {
  1797. return ${*$main::FTP_chan}{'net_cmd_code'};
  1798. }
  1799. sub ftp_error() {
  1800. my $code = ftp_code();
  1801. return ( $code =~ /^[45]/ ) ? 1 : 0;
  1802. }
  1803. # ---------------------------------------------------------------------------
  1804. # utility functions
  1805. # ---------------------------------------------------------------------------
  1806. sub ssh_cmd($) {
  1807. my $cmd = shift;
  1808. my ( $msg, $stat );
  1809. my $ecmd = "$conf::ssh $conf::ssh_options $conf::target "
  1810. . "-l $conf::targetlogin \'cd $main::current_targetdir; $cmd\'";
  1811. debug("executing $ecmd");
  1812. $SIG{"ALRM"} = sub { die "timeout in ssh command\n" };
  1813. alarm($conf::remote_timeout);
  1814. eval { $msg = `$ecmd 2>&1`; };
  1815. alarm(0);
  1816. if ($@) {
  1817. $msg = $@;
  1818. $stat = 1;
  1819. } else {
  1820. $stat = $?;
  1821. }
  1822. return ( $msg, $stat );
  1823. } ## end sub ssh_cmd($)
  1824. sub scp_cmd(@) {
  1825. my ( $msg, $stat );
  1826. my $ecmd = "$conf::scp $conf::ssh_options @_ "
  1827. . "$conf::targetlogin\@$conf::target:$main::current_targetdir";
  1828. debug("executing $ecmd");
  1829. $SIG{"ALRM"} = sub { die "timeout in scp\n" };
  1830. alarm($conf::remote_timeout);
  1831. eval { $msg = `$ecmd 2>&1`; };
  1832. alarm(0);
  1833. if ($@) {
  1834. $msg = $@;
  1835. $stat = 1;
  1836. } else {
  1837. $stat = $?;
  1838. }
  1839. return ( $msg, $stat );
  1840. } ## end sub scp_cmd(@)
  1841. #
  1842. # check if target is alive (code stolen from Net::Ping.pm)
  1843. #
  1844. sub check_alive(;$) {
  1845. my $timeout = shift;
  1846. my ( $saddr, $ret, $target_ip );
  1847. local (*PINGSOCK);
  1848. if ( $conf::upload_method eq "copy" ) {
  1849. format_status_num( $main::last_ping_time, time );
  1850. $main::target_up = 1;
  1851. return;
  1852. }
  1853. $timeout ||= 30;
  1854. if ( !( $target_ip = ( gethostbyname($conf::target) )[4] ) ) {
  1855. msg( "log", "Cannot get IP address of $conf::target\n" );
  1856. $ret = 0;
  1857. goto out;
  1858. }
  1859. $saddr = pack( 'S n a4 x8', AF_INET, $main::echo_port, $target_ip );
  1860. $SIG{'ALRM'} = sub { die };
  1861. alarm($timeout);
  1862. $ret = $main::tcp_proto; # avoid warnings about unused variable
  1863. $ret = 0;
  1864. eval <<'EOM' ;
  1865. return unless socket( PINGSOCK, PF_INET, SOCK_STREAM, $main::tcp_proto );
  1866. return unless connect( PINGSOCK, $saddr );
  1867. $ret = 1;
  1868. EOM
  1869. alarm(0);
  1870. close(PINGSOCK);
  1871. msg( "log", "pinging $conf::target: " . ( $ret ? "ok" : "down" ) . "\n" );
  1872. out:
  1873. $main::target_up = $ret ? "1" : "0";
  1874. format_status_num( $main::last_ping_time, time );
  1875. write_status_file() if $conf::statusdelay;
  1876. } ## end sub check_alive(;$)
  1877. #
  1878. # check if incoming dir on target is writable
  1879. #
  1880. sub check_incoming_writable() {
  1881. my $testfile = ".debianqueued-testfile";
  1882. my ( $msg, $stat );
  1883. if ( $conf::upload_method eq "ssh" ) {
  1884. ( $msg, $stat ) =
  1885. ssh_cmd( "rm -f $testfile; touch $testfile; " . "rm -f $testfile" );
  1886. } elsif ( $conf::upload_method eq "ftp" ) {
  1887. my $file = "junk-for-writable-test-" . format_time();
  1888. $file =~ s/[ :.]/-/g;
  1889. local (*F);
  1890. open( F, ">", $file );
  1891. close(F);
  1892. my $rv;
  1893. ( $rv, $msg ) = ftp_cmd( "put", $file );
  1894. $stat = 0;
  1895. $msg = "" if !defined $msg;
  1896. unlink $file;
  1897. ftp_cmd( "delete", $file );
  1898. } elsif ( $conf::upload_method eq "copy" ) {
  1899. unless(POSIX::access($main::current_targetdir, &POSIX::W_OK)) {
  1900. $msg = "No write access: $!";
  1901. $stat = 1;
  1902. }
  1903. }
  1904. chomp($msg);
  1905. debug("exit status: $stat, output was: $msg");
  1906. if ( !$stat ) {
  1907. # change incoming_writable only if ssh didn't return an error
  1908. $main::incoming_writable =
  1909. ( $msg =~ /(permission denied|read-?only file|cannot create)/i )
  1910. ? "0"
  1911. : "1";
  1912. } else {
  1913. debug("local error, keeping old status");
  1914. }
  1915. debug("incoming_writable = $main::incoming_writable");
  1916. write_status_file() if $conf::statusdelay;
  1917. return $main::incoming_writable;
  1918. } ## end sub check_incoming_writable()
  1919. #
  1920. # remove a list of files, log failing ones
  1921. #
  1922. sub rm(@) {
  1923. my $done = 0;
  1924. foreach (@_) {
  1925. ( unlink $_ and ++$done )
  1926. or $! == ENOENT
  1927. or msg( "log", "Could not delete $_: $!\n" );
  1928. }
  1929. return $done;
  1930. } ## end sub rm(@)
  1931. #
  1932. # get md5 checksum of a file
  1933. #
  1934. sub md5sum($) {
  1935. my $file = shift;
  1936. my $md5 = Digest::MD5->new;
  1937. open my $fh, "<", $file or return "";
  1938. $md5->addfile($fh);
  1939. close $fh;
  1940. return $md5->hexdigest;
  1941. } ## end sub md5sum($)
  1942. #
  1943. # output a messages to several destinations
  1944. #
  1945. # first arg is a comma-separated list of destinations; valid are "log"
  1946. # and "mail"; rest is stuff to be printed, just as with print
  1947. #
  1948. sub msg($@) {
  1949. my @dest = split( ',', shift );
  1950. if ( grep /log/, @dest ) {
  1951. my $now = format_time();
  1952. print LOG "$now ", @_;
  1953. }
  1954. if ( grep /mail/, @dest ) {
  1955. $main::mail_text .= join( '', @_ );
  1956. }
  1957. } ## end sub msg($@)
  1958. #
  1959. # print a debug messages, if $debug is true
  1960. #
  1961. sub debug(@) {
  1962. return if !$conf::debug;
  1963. my $now = format_time();
  1964. print LOG "$now DEBUG ", @_, "\n";
  1965. }
  1966. #
  1967. # intialize the "mail" destination of msg() (this clears text,
  1968. # address, subject, ...)
  1969. #
  1970. sub init_mail(;$) {
  1971. my $file = shift;
  1972. $main::mail_addr = "";
  1973. $main::mail_text = "";
  1974. %main::packages = ();
  1975. $main::mail_subject = $file ? "Processing of $file" : "";
  1976. } ## end sub init_mail(;$)
  1977. #
  1978. # finalize mail to be sent from msg(): check if something present, and
  1979. # then send out
  1980. #
  1981. sub finish_mail() {
  1982. debug("No mail for $main::mail_addr")
  1983. if $main::mail_addr && !$main::mail_text;
  1984. return unless $main::mail_addr && $main::mail_text;
  1985. if ( !send_mail( $main::mail_addr, $main::mail_subject, $main::mail_text ) )
  1986. {
  1987. # store this mail in memory so it isn't lost if executing sendmail
  1988. # failed.
  1989. push(
  1990. @main::stored_mails,
  1991. {
  1992. addr => $main::mail_addr,
  1993. subject => $main::mail_subject,
  1994. text => $main::mail_text
  1995. }
  1996. );
  1997. } ## end if ( !send_mail( $main::mail_addr...
  1998. init_mail();
  1999. # try to send out stored mails
  2000. my $mailref;
  2001. while ( $mailref = shift(@main::stored_mails) ) {
  2002. if (
  2003. !send_mail( $mailref->{'addr'}, $mailref->{'subject'},
  2004. $mailref->{'text'} )
  2005. )
  2006. {
  2007. unshift( @main::stored_mails, $mailref );
  2008. last;
  2009. } ## end if ( !send_mail( $mailref...
  2010. } ## end while ( $mailref = shift(...
  2011. } ## end sub finish_mail()
  2012. #
  2013. # send one mail
  2014. #
  2015. sub send_mail($$$) {
  2016. my $addr = shift;
  2017. my $subject = shift;
  2018. my $text = shift;
  2019. my $package =
  2020. keys %main::packages ? join( ' ', keys %main::packages ) : "";
  2021. use Email::Sender::Simple;
  2022. if ($conf::overridemail) {
  2023. $addr = $conf::overridemail;
  2024. }
  2025. my $date = sprintf "%s",
  2026. strftime( "%a, %d %b %Y %T %z", ( localtime(time) ) );
  2027. my $message = <<__MESSAGE__;
  2028. To: $addr
  2029. From: Debian FTP Masters <ftpmaster\@ftp-master.debian.org>
  2030. Subject: $subject
  2031. Date: $date
  2032. X-Debian: DAK
  2033. X-DAK: DAK
  2034. Precedence: bulk
  2035. Auto-Submitted: auto-generated
  2036. __MESSAGE__
  2037. if ( length $package ) {
  2038. $message .= "X-Debian-Package: $package\n";
  2039. }
  2040. $message .= "\n$text";
  2041. $message .= "\nGreetings,\n\n\tYour Debian queue daemon (running on host $main::hostname)\n";
  2042. return Email::Sender::Simple->try_to_send($message);
  2043. } ## end sub send_mail($$$)
  2044. #
  2045. # try to find a mail address for a name in the keyrings
  2046. #
  2047. sub try_to_get_mail_addr($$) {
  2048. my $name = shift;
  2049. my $listref = shift;
  2050. @$listref = ();
  2051. open( F,
  2052. "$conf::gpg --no-options --batch --no-default-keyring "
  2053. . "--always-trust --keyring "
  2054. . join( " --keyring ", @conf::keyrings )
  2055. . " --list-keys |"
  2056. ) or return "";
  2057. while (<F>) {
  2058. if ( /^pub / && / $name / ) {
  2059. /<([^>]*)>/;
  2060. push( @$listref, $1 );
  2061. }
  2062. } ## end while (<F>)
  2063. close(F);
  2064. return ( @$listref >= 1 ) ? $listref->[0] : "";
  2065. } ## end sub try_to_get_mail_addr($$)
  2066. #
  2067. # return current time as string
  2068. #
  2069. sub format_time() {
  2070. my $t;
  2071. # omit weekday and year for brevity
  2072. ( $t = localtime ) =~ /^\w+\s(.*)\s\d+$/;
  2073. return $1;
  2074. } ## end sub format_time()
  2075. sub print_time($) {
  2076. my $secs = shift;
  2077. my $hours = int( $secs / ( 60 * 60 ) );
  2078. $secs -= $hours * 60 * 60;
  2079. return sprintf "%d:%02d:%02d", $hours, int( $secs / 60 ), $secs % 60;
  2080. } ## end sub print_time($)
  2081. #
  2082. # block some signals during queue processing
  2083. #
  2084. # This is just to avoid data inconsistency or uploads being aborted in the
  2085. # middle. Only "soft" signals are blocked, i.e. SIGINT and SIGTERM, try harder
  2086. # ones if you really want to kill the daemon at once.
  2087. #
  2088. sub block_signals() {
  2089. POSIX::sigprocmask( SIG_BLOCK, $main::block_sigset );
  2090. }
  2091. sub unblock_signals() {
  2092. POSIX::sigprocmask( SIG_UNBLOCK, $main::block_sigset );
  2093. }
  2094. #
  2095. # process SIGHUP: close log file and reopen it (for logfile cycling)
  2096. #
  2097. sub close_log($) {
  2098. close(LOG);
  2099. close(STDOUT);
  2100. close(STDERR);
  2101. open( LOG, ">>", $conf::logfile )
  2102. or die "Cannot open my logfile $conf::logfile: $!\n";
  2103. chmod( 0644, $conf::logfile )
  2104. or msg( "log", "Cannot set modes of $conf::logfile: $!\n" );
  2105. select( ( select(LOG), $| = 1 )[0] );
  2106. open( STDOUT, ">&", \*LOG )
  2107. or msg( "log",
  2108. "$main::progname: Can't redirect stdout to " . "$conf::logfile: $!\n" );
  2109. open( STDERR, ">&", \*LOG )
  2110. or msg( "log",
  2111. "$main::progname: Can't redirect stderr to " . "$conf::logfile: $!\n" );
  2112. msg( "log", "Restart after SIGHUP\n" );
  2113. } ## end sub close_log($)
  2114. #
  2115. # process SIGCHLD: check if it was our statusd process
  2116. #
  2117. sub kid_died($) {
  2118. my $pid;
  2119. # reap statusd, so that it's no zombie when we try to kill(0) it
  2120. waitpid( $main::statusd_pid, WNOHANG );
  2121. # Uncomment the following line if your Perl uses unreliable System V signal
  2122. # (i.e. if handlers reset to default if the signal is delivered).
  2123. # (Unfortunately, the re-setup can't be done in any case, since on some
  2124. # systems this will cause the SIGCHLD to be delivered again if there are
  2125. # still unreaped children :-(( )
  2126. # $SIG{"CHLD"} = \&kid_died; # resetup handler for SysV
  2127. } ## end sub kid_died($)
  2128. sub restart_statusd() {
  2129. # restart statusd if it died
  2130. if ( !kill( 0, $main::statusd_pid ) ) {
  2131. close(STATUSD); # close out pipe end
  2132. $main::statusd_pid = fork_statusd();
  2133. }
  2134. } ## end sub restart_statusd()
  2135. #
  2136. # process a fatal signal: cleanup and exit
  2137. #
  2138. sub fatal_signal($) {
  2139. my $signame = shift;
  2140. my $sig;
  2141. # avoid recursions of fatal_signal in case of BSD signals
  2142. foreach $sig (qw( ILL ABRT BUS FPE SEGV PIPE )) {
  2143. $SIG{$sig} = "DEFAULT";
  2144. }
  2145. if ( $$ == $main::maind_pid ) {
  2146. # only the main daemon should do this
  2147. kill( $main::signo{"TERM"}, $main::statusd_pid )
  2148. if defined $main::statusd_pid;
  2149. unlink( $conf::statusfile, $conf::pidfile );
  2150. } ## end if ( $$ == $main::maind_pid)
  2151. msg( "log", "Caught SIG$signame -- exiting (pid $$)\n" );
  2152. exit 1;
  2153. } ## end sub fatal_signal($)
  2154. # Local Variables:
  2155. # tab-width: 4
  2156. # fill-column: 78
  2157. # End: