sshactive: improve recording of last active times
[girocco.git] / taskd / taskd.pl
blob4fc378a6911f129fedd71e2bd7aa934ae6272470
1 #!/usr/bin/perl
3 # taskd - Clone repositories on request
5 # taskd is Girocco mirroring servant; it processes requests for clones
6 # of given URLs received over its socket.
8 # When a request is received, new process is spawned that sets up
9 # the repository and reports further progress
10 # to .clonelog within the repository. In case the clone fails,
11 # .clone_failed is touched and .clone_in_progress is removed.
13 # Clone protocol:
14 # Alice sets up repository and touches .cloning
15 # Alice opens connection to Bob
16 # Alice sends project name through the connection
17 # Bob opens the repository and sends error code if there is a problem
18 # Bob closes connection
19 # Alice polls .clonelog in case of success.
20 # If Alice reads "@OVER@" from .clonelog, it stops polling.
22 # Ref-change protocol:
23 # Alice opens connection to Bob
24 # Alice sends ref-change command for each changed ref
25 # Alice closes connection
26 # Bob sends out notifications
28 # Based on perlipc example.
30 use strict;
31 use warnings;
33 use Socket;
34 use Errno;
35 use POSIX ":sys_wait_h";
36 use File::Basename;
38 use lib dirname($0);
39 use Girocco::Config;
40 use Girocco::Notify;
41 use Girocco::Project;
42 use Girocco::User;
44 $| = 1;
46 sub logmsg { print '['.(scalar localtime)."] $0 $$: @_\n" }
48 my $NAME = $Girocco::Config::chroot.'/etc/taskd.socket';
49 my $uaddr = sockaddr_un($NAME);
51 socket(Server, PF_UNIX, SOCK_STREAM, 0) or die "socket: $!";
52 unlink($NAME);
53 bind(Server, $uaddr) or die "bind: $!";
54 listen(Server, SOMAXCONN) or die "listen: $!";
55 chmod 0666, $NAME or die "chmod: $!";
57 sub REAPER {
58 local $!;
59 my $child;
60 my $waitedpid;
61 while (($waitedpid = waitpid(-1, WNOHANG)) > 0) {
62 logmsg "reaped $waitedpid" . ($? ? " with exit $?" : '');
64 $SIG{CHLD} = \&REAPER; # loathe sysV
67 $SIG{CHLD} = \&REAPER; # Apollo 440
69 sub spawn {
70 my $coderef = shift;
72 my $pid = fork;
73 if (not defined $pid) {
74 logmsg "cannot fork: $!";
75 return;
76 } elsif ($pid) {
77 logmsg "begat $pid";
78 return; # I'm the parent
81 $SIG{CHLD} = 'DEFAULT';
83 open STDIN, "<&Client" or die "can't dup client to stdin";
84 open STDOUT, ">&Client" or die "can't dup client to stdout";
85 exit &$coderef();
88 sub clone {
89 my ($name) = @_;
90 my $proj = Girocco::Project->load($name);
91 $proj or die "failed to load project $name";
92 $proj->{clone_in_progress} or die "project $name is not marked for cloning";
93 $proj->{clone_logged} and die "project $name is already being cloned";
94 print STDERR "cloning $name\n";
95 open STDOUT, ">".$Girocco::Config::reporoot."/".$name.".git/.clonelog" or die "cannot open clonelog: $!";
96 open STDERR, ">&STDOUT";
97 open STDIN, "</dev/null";
98 exec $Girocco::Config::basedir.'/taskd/clone.sh', "$name.git" or die "exec failed: $!";
101 sub ref_change {
102 my ($arg) = @_;
103 my ($username, $name, $oldrev, $newrev, $ref) = split(/\s+/, $arg);
105 my $proj = Girocco::Project->load($name);
106 $proj or die "failed to load project $name";
108 my $user;
109 if ($username && $username !~ /^%.*%$/) {
110 $user = Girocco::User->load($username);
111 $user or die "failed to load user $username";
114 print STDERR "ref-change $username $name ($ref: $oldrev -> $newrev)\n";
115 open STDIN, "</dev/null";
116 Girocco::Notify::ref_change($proj, $user, $ref, $oldrev, $newrev);
117 return 0;
120 while (1) {
121 unless (accept(Client, Server)) {
122 logmsg "accept failed: $!" unless $!{EINTR};
123 next;
125 logmsg "connection on $NAME";
126 spawn sub {
127 my $inp = <>;
128 chomp $inp;
129 $inp or exit 0; # ignore empty connects
130 my ($cmd, $arg) = $inp =~ /^([a-zA-Z0-9-]+)\s+(.*)$/;
131 if ($cmd eq 'clone') {
132 clone($arg);
133 } elsif ($cmd eq 'ref-change') {
134 ref_change($arg);
135 } else {
136 die "unknown command: $cmd";
139 close Client;
140 sleep 1;