diff --git a/99-ci-maipo/Dockerfile b/99-ci-maipo/Dockerfile new file mode 100644 index 0000000..3f97390 --- /dev/null +++ b/99-ci-maipo/Dockerfile @@ -0,0 +1,109 @@ +FROM centos:centos7 +MAINTAINER Kolab Systems + +ADD http://obs.kolabsys.com/repositories/Kolab:/Development/CentOS_7/Kolab:Development.repo \ + /etc/yum.repos.d/Kolab:Development.repo + +ADD http://obs.kolabsys.com/repositories/Kolab:/3.4/CentOS_7/Kolab:3.4.repo \ + /etc/yum.repos.d/Kolab:3.4.repo + +ADD http://obs.kolabsys.com/repositories/Kolab:/3.4:/Updates/CentOS_7/Kolab:3.4:Updates.repo \ + /etc/yum.repos.d/Kolab:3.4:Updates.repo + +RUN echo "priority=60" >> /etc/yum.repos.d/Kolab:Development.repo + +RUN echo "priority=60" >> /etc/yum.repos.d/Kolab:3.4.repo + +RUN echo "priority=60" >> /etc/yum.repos.d/Kolab:3.4:Updates.repo + +RUN rpm --import https://ssl.kolabsys.com/community.asc + +ADD http://download.opensuse.org/repositories/openSUSE:Tools/CentOS_7/openSUSE:Tools.repo \ + /etc/yum.repos.d/openSUSE:Tools.repo + +RUN rpm --import http://download.opensuse.org/repositories/openSUSE:/Tools/CentOS_7/repodata/repomd.xml.key + +RUN sed -i -e '/tsflags=nodocs/d' /etc/yum.conf && \ + sed -r -i -e 's/enabled\s*=\s*1/enabled=0/g' /etc/yum/pluginconf.d/fastestmirror.conf + +#RUN yum -y reinstall \* + +ENV IMAGE maipo + +ADD /dbus.service /etc/systemd/system/dbus.service +RUN ln -sf dbus.service /etc/systemd/system/messagebus.service + +ADD /httpd.service /etc/systemd/system/httpd.service + +ADD /systemctl /usr/bin/systemctl +ADD /systemctl-socket-daemon /usr/bin/systemctl-socket-daemon + +RUN chmod -v a+rx \ + /usr/bin/systemctl \ + /usr/bin/systemctl-socket-daemon + +ADD /vimrc /root/.vimrc + +RUN yum clean all && \ + yum -y install epel-release && \ + sed -i \ + -e 's/#baseurl/baseurl/g' \ + -e '/^mirrorlist/d' \ + /etc/yum.repos.d/epel*.repo && \ + yum -y install \ + ant \ + bind-utils \ + build \ + git \ + lsof \ + net-tools \ + nmap-ncat \ + openldap-clients \ + osc \ + perl-Data-Dumper \ + perl-Digest-MD5 \ + perl-TimeDate \ + php-phpunit-PHPUnit-Selenium \ + psmisc \ + rpmdevtools \ + screen \ + strace \ + sudo \ + telnet \ + traceroute \ + vim-enhanced \ + yum-utils && \ + yum clean all + +RUN sed -i -e '/requiretty/d' /etc/sudoers + +WORKDIR /srv +RUN git clone https://github.com/google/closure-compiler.git closure-compiler.git +WORKDIR /srv/closure-compiler.git +RUN ant jar + +ADD http://mirror.kolabsys.com/pub/releases/selenium-server-standalone.jar /usr/local/lib/selenium-server-standalone.jar + +#WORKDIR /srv +#RUN git clone https://github.com/SeleniumHQ/selenium.git selenium.git +#WORKDIR /srv/selenium.git +#RUN ./go --trace --verbose build +#RUN ./go selenium-server-standalone + +ADD http://mirror.kolabsys.com/pub/releases/phantomjs-1.9.7-linux-x86_64.tar.bz2 /srv/ +WORKDIR /srv +RUN tar jxvf phantomjs-1.9.7-linux-x86_64.tar.bz2 +RUN cp phantomjs-1.9.7-linux-x86_64/bin/phantomjs /usr/local/bin/ && \ + rm -rf phantomjs-1.9.7-linux-x86_64* + +#WORKDIR /srv/ +#RUN git clone https://github.com/ariya/phantomjs.git phantomjs.git +#WORKDIR /srv/phantomjs.git +#RUN ./build.sh --confirm + +ADD /functions.sh / +ADD entrypoint.sh / + +RUN chmod 755 /entrypoint.sh + +ENTRYPOINT [ "/entrypoint.sh" ] diff --git a/99-ci-maipo/dbus.service b/99-ci-maipo/dbus.service new file mode 100644 index 0000000..0be95c4 --- /dev/null +++ b/99-ci-maipo/dbus.service @@ -0,0 +1,16 @@ +[Unit] +Description=D-Bus System Message Bus +Requires=dbus.socket +After=syslog.target + +[Service] +PIDFile=/var/run/messagebus.pid +ExecStartPre=/bin/mkdir -p /var/run/dbus +ExecStartPre=/bin/chmod g+w /var/run/ /var/run/dbus/ +ExecStart=/bin/dbus-daemon --system --fork +ExecReload=/bin/dbus-send --print-reply --system --type=method_call --dest=org.freedesktop.DBus / org.freedesktop.DBus.ReloadConfig +ExecStopPost=/bin/rm -f /var/run/messagebus.pid +OOMScoreAdjust=-900 +User=dbus +Group=root +PermissionsStartOnly=true diff --git a/99-ci-maipo/entrypoint.sh b/99-ci-maipo/entrypoint.sh new file mode 100755 index 0000000..a099eca --- /dev/null +++ b/99-ci-maipo/entrypoint.sh @@ -0,0 +1,148 @@ +#!/bin/bash + +if [ ! -d "/srv/stick.git" ]; then + git clone https://git.kolab.org/diffusion/QA/stick.git /srv/stick.git +elif [ -z "${PS1}" ]; then + pushd /srv/stick.git + git remote set-url origin https://git.kolab.org/diffusion/QA/stick.git + git fetch origin + git reset --hard origin/master + git clean -d -f -x + popd +fi + +source /functions.sh + +export TEST_BUILD=${TEST_BUILD:-0} +export TEST_FUNCTIONAL=${TEST_FUNCTIONAL:-0} +export TEST_INTEGRATION=${TEST_INTEGRATION:-0} +export TEST_PERFORMANCE=${TEST_PERFORMANCE:-0} +export TEST_UNIT=${TEST_UNIT:-0} +export TEST_OBS=${TEST_OBS:-0} + +# If PS1 is set, we're interactive +if [ ! -z "${PS1}" ]; then + # Set a sensible prompt + PS1='[\u@${IMAGE} \W]\$ ' + + export GIT_PS1_SHOWDIRTYSTATE=1 + export GIT_PS1_SHOWUNTRACKEDFILES=1 + export GIT_PS1_SHOWUPSTREAM="auto verbose" + + if [ ! -f "/etc/bash_completion" ]; then + if [ -f "/etc/bash_completion.d/git" ]; then + . /etc/bash_completion.d/git + PS1='[\u@${IMAGE} \W$(__git_ps1 " (%s)")]\$ ' + fi + else + PS1='[\u@${IMAGE} \W$(__git_ps1 " (%s)")]\$ ' + fi + + export PS1 + + PROMPT_COMMAND="echo -ne '\033]0;${IMAGE} (in ${HOSTNAME})\007'" + + if [ -f "/usr/share/git-core/contrib/completion/git-prompt.sh" ]; then + source /usr/share/git-core/contrib/completion/git-prompt.sh + fi +fi + +if [ ! -d "/srv/${PACKAGE}.git" ]; then + git clone ${RO_URI} /srv/${PACKAGE}.git + + pushd /srv/${PACKAGE}.git + + for branch in $(git branch -la | sed -e 's/^* //g' -e 's/ //g' -e '/remotes\/origin\/HEAD/d' -e 's|remotes/origin/||g' | sort -u); do + git checkout $branch + done + + popd +else + pushd /srv/${PACKAGE}.git + + git remote set-url origin ${RO_URI} + git fetch origin + git reset --hard origin/master + git clean -d -f -x + + for branch in $(git branch -la | sed -e 's/^* //g' -e '/\(detached from/d' -e 's/ //g' -e '/remotes\/origin\/HEAD/d' -e 's|remotes/origin/||g' | sort -u); do + git checkout $branch + done + + popd +fi + +retval=0 + +if [ -x "$(which yum 2>/dev/null)" ]; then + yum clean metadata; retval=$(( ${retval} + $? )) + yum -y update; retval=$(( ${retval} + $? )) + rpmdev-setuptree; retval=$(( ${retval} + $? )) +elif [ -x "$(which apt-get 2>/dev/null)" ]; then + apt-get update; retval=$(( ${retval} + $? )) +fi + +pushd /srv/${PACKAGE}.git + +if [ ! -z "${COMMIT}" ]; then + git checkout ${COMMIT}; retval=$(( ${retval} + $? )) +fi + +# TODO: A differential has a base commit. + +if [ ${retval} -ne 0 ]; then + _report + exit 1 +fi + +if [ -x "../stick.git/drydocker/${PACKAGE}/test_build.sh" -a ${TEST_BUILD} -eq 1 ]; then + retval=$(_shell ../stick.git/drydocker/${PACKAGE}/test_build.sh) + if [ ${retval} -ne 0 ]; then + _report + exit 1 + fi +fi + +if [ -x "../stick.git/drydocker/${PACKAGE}/test_unit.sh" -a ${TEST_UNIT} -eq 1 ]; then + retval=$(_shell ../stick.git/drydocker/${PACKAGE}/test_unit.sh) + if [ ${retval} -ne 0 ]; then + _report + exit 1 + fi +fi + +if [ -x "../stick.git/drydocker/${PACKAGE}/test_functional.sh" -a ${TEST_FUNCTIONAL} -eq 1 ]; then + retval=$(_shell ../stick.git/drydocker/${PACKAGE}/test_functional.sh) + if [ ${retval} -ne 0 ]; then + _report + exit 1 + fi +fi + +if [ -x "../stick.git/drydocker/${PACKAGE}/test_obs.sh" ]; then + retval=$(_shell ../stick.git/drydocker/${PACKAGE}/test_obs.sh) + if [ ${retval} -ne 0 ]; then + _report + exit 1 + fi +fi + +if [ -x "../stick.git/drydocker/${PACKAGE}/test_integration.sh" -a ${TEST_INTEGRATION} -eq 1 ]; then + retval=$(_shell ../stick.git/drydocker/${PACKAGE}/test_integration.sh) + if [ ${retval} -ne 0 ]; then + _report + exit 1 + fi +fi + +if [ -x "../stick.git/drydocker/${PACKAGE}/test_obs_checkin.sh" ]; then + retval=$(_shell ../stick.git/drydocker/${PACKAGE}/test_obs_checkin.sh) + if [ ${retval} -ne 0 ]; then + _report + exit 1 + fi +fi + +popd + +_report diff --git a/99-ci-maipo/functions.sh b/99-ci-maipo/functions.sh new file mode 100644 index 0000000..27a9d32 --- /dev/null +++ b/99-ci-maipo/functions.sh @@ -0,0 +1,65 @@ +#!/bin/bash + +# Create 3 as an alias for 1, so the _shell function +# can output data without the caller getting the input. +exec 3>&1 + +function _report { + echo $(printf '%0.1s' "="{1..72}) + cat ${TMPDIR:-/tmp}/report.log + rm -rf ${TMPDIR:-/tmp}/report.log + echo $(printf '%0.1s' "="{1..72}) +} + +export -f _report + +function _report_msg { + printf "%*s" $(( ( ${BASH_SUBSHELL} - 1 ) * 4 )) " " >> ${TMPDIR:-/tmp}/report.log + echo "$@" >> ${TMPDIR:-/tmp}/report.log +} + +export -f _report_msg + +function _shell { + revision=$(git rev-parse HEAD 2>/dev/null) + if [ -z "${revision}" ]; then + pushd /srv/${PACKAGE}.git >/dev/null 2>&1 3>&1 + revision=$(git rev-parse HEAD 2>/dev/null) + popd >/dev/null 2>&1 3>&1 + fi + + if [ -z "${revision}" ]; then + revision=unknown + fi + + echo "Running $@ ..." >&3 + $@ >&3 2>&3 ; retval=$? + + if [ ${retval} -eq 0 ]; then + _report_msg "Running '$@' OK (at ${revision})" + echo "Running $@ OK (at ${revision})" >&3 + else + _report_msg "Running '$@' FAILED (at ${revision})" + echo "Running $@ FAILED (at ${revision})" >&3 + fi + + echo ${retval} +} + +export -f _shell + +function _install_package { + if [ -x "$(which yum 2>/dev/null)" ]; then + yum -y install $@ + elif [ -x "$(which apt-get 2>/dev/null)" ]; then + apt-get -y install $@ + fi +} + +function _install_package_builddep { + if [ -x "$(which yum 2>/dev/null)" ]; then + yum-builddep -y --disablerepo=openSUSE_Tools $@ + elif [ -x "$(which apt-get 2>/dev/null)" ]; then + apt-get -y build-dep $@ + fi +} diff --git a/99-ci-maipo/httpd.service b/99-ci-maipo/httpd.service new file mode 100644 index 0000000..ba7ec71 --- /dev/null +++ b/99-ci-maipo/httpd.service @@ -0,0 +1,4 @@ +.include /lib/systemd/system/httpd.service +[Service] +PIDFile=/var/run/httpd/httpd.pid + diff --git a/99-ci-maipo/sudoers.drydock-worker b/99-ci-maipo/sudoers.drydock-worker new file mode 100644 index 0000000..489b82b --- /dev/null +++ b/99-ci-maipo/sudoers.drydock-worker @@ -0,0 +1 @@ +drydock-worker ALL=(ALL) NOPASSWD: ALL diff --git a/99-ci-maipo/systemctl b/99-ci-maipo/systemctl new file mode 100644 index 0000000..6a15c0b --- /dev/null +++ b/99-ci-maipo/systemctl @@ -0,0 +1,680 @@ +#!/usr/bin/perl + +# Copyright 2014 Jan Pazdziora +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +use strict; +use warnings FATAL => 'all'; + +use IO::File (); +use IO::Dir (); +use IO::Socket::UNIX (); +use Socket (); +use Data::Dumper (); +use POSIX (); +use Time::HiRes qw(sleep time); + +sub log_command { + local * LOG; + open(LOG, '>>', '/var/log/systemctl.log'); + print LOG @_; + close LOG; +} +log_command("[@ARGV]\n"); +shift if @ARGV and $ARGV[0] eq '-q'; + +for (my $i = 0; $i < @ARGV; $i++) { + if ($ARGV[$i] eq '--ignore-dependencies') { + splice @ARGV, $i, 1; + last; + } +} +if (@ARGV == 1 and $ARGV[0] eq 'daemon-reload') { + exit 0; +} +if (@ARGV == 2 and $ARGV[0] eq '--system' and $ARGV[1] eq 'daemon-reload') { + exit 0; +} + +for (keys %ENV) { + delete $ENV{$_} unless $_ eq '_SYSTEMCTL_LITE_STARTING'; +} + +my $RUNNING_DIR = '/run/systemctl-lite-running'; +if (not -d $RUNNING_DIR) { + mkdir $RUNNING_DIR; +} +my $ENABLED_DIR = '/etc/systemctl-lite-enabled'; +if (@ARGV == 1) { + if ($ARGV[0] eq 'start-enabled' and -d $ENABLED_DIR) { + local * ENABLED; + opendir ENABLED, $ENABLED_DIR; + my %services; + while (defined(my $f = readdir ENABLED)) { + next if $f eq '.' or $f eq '..'; + my $modified = (stat "$ENABLED_DIR/$f")[9]; + if (defined $modified) { + $services{$f} = $modified; + } + } + close ENABLED; + for my $s (sort { $services{$a} <=> $services{$b} or $a cmp $b } keys %services) { + print "Starting [$s]\n"; + system $0, 'start', $s; + exit ($? >> 8) if ($? >> 8); + } + } + if ($ARGV[0] eq 'stop-running' and -d $RUNNING_DIR) { + local * RUNNING; + opendir RUNNING, $RUNNING_DIR; + my %services; + while (defined(my $f = readdir RUNNING)) { + next if $f eq '.' or $f eq '..'; + my $modified = (stat "$RUNNING_DIR/$f")[9]; + if (defined $modified) { + my $trimmed = $f; + $trimmed =~ s/\..+$//; + $services{$trimmed} = $modified; + } + } + close RUNNING; + for my $s (sort { $services{$b} <=> $services{$a} or $a cmp $b } keys %services) { + system $0, 'stop', $s; + } + } + exit; +} + +if (@ARGV != 2) { + die "Usage: $0 (start|stop|status|...) (service|target|socket thing)\n"; +} + +my ($COMMAND, $SERVICE) = @ARGV; +my $TYPE = 'service'; +if ($SERVICE =~ /\.(target|socket)$/) { + $TYPE = $1; +} elsif (not $SERVICE =~ /\.service$/) { + $SERVICE .= '.service'; +} + +my @PATHS = ( + '/etc/systemd/system', + '/run/systemd/system', + '/usr/lib/systemd/system', +); + +my $FULL_SERVICE = $SERVICE; +my $INSTANCE = undef; + +my $file = undef; +if ($SERVICE =~ s/\@(.+)\.service$/\@.service/) { + $INSTANCE = $1; +} +for my $p (@PATHS) { + if (-e "$p/$SERVICE") { + if (-l "$p/$SERVICE") { + my $new_service = readlink("$p/$SERVICE"); + log_command("Service [$p/$SERVICE] is a symlink to [$new_service]\n"); + $new_service =~ s!^.*/!!; + $SERVICE = $new_service; + } + $file = "$p/$SERVICE"; + last; + } +} + +if ($SERVICE =~ s/\@(.+)\.service$/\@.service/) { + $INSTANCE = $1; + for my $p (@PATHS) { + if (-f "$p/$SERVICE") { + $file = "$p/$SERVICE"; + last; + } + } +} + +if (not defined $file) { + if ($COMMAND eq 'is-enabled') { + exit 1; + } + if ($COMMAND eq 'is-active') { + exit 3; + } + warn "No service definition found for [$FULL_SERVICE].\n"; + exit 2; +} + +sub parse_file { + my ($file, $data) = @_; + if (-d $file) { + my $op; + if ($file =~ /\.requires$/) { + $op = 'Unit.Requires'; + } elsif ($file =~ /\.wants$/) { + $op = 'Unit.Wants'; + } else { + die "Unknown directory [$file].\n"; + } + my $dh = new IO::Dir($file); + while (defined(my $de = $dh->read)) { + next if $de eq '.' or $de eq '..'; + push @{$data->{$op}}, $de unless $de =~ /\.target$/; + } + $dh->close; + return; + } + my $fh = new IO::File($file); + my $section = 'undefined'; + while (my $line = <$fh>) { + chomp $line; + if ($line =~ /^\[(.+)\]\s*$/) { + $section = $1; + next; + } + next if $line =~ /^\s*(#|$)/; + if ($line =~ /^\.include\s(.+)/) { + parse_file($1, $data); + next; + } + my ($key, $value) = split /=/, $line, 2; + if (defined $INSTANCE) { + $value =~ s/\%i/$INSTANCE/g; + } + if ($key eq 'EnvironmentFile') { + if ($value eq '') { + delete $data->{"$section.$key"}; + } else { + push @{ $data->{"$section.$key"} }, $value; + } + } elsif ($key =~ /^(Wants|Requires)$/) { + push @{ $data->{"$section.$key"} }, $value unless $value =~ /\.target$/; + } elsif ($key =~ /^(ExecStart(Pre|Post)|ExecReload|ExecStop(Pre|Post)?)$/) { + push @{ $data->{"$section.$key"} }, $value; + } else { + $data->{"$section.$key"} = $value; + } + } + $fh->close; + if (defined $data->{'Service.ExecStart'} and not defined $data->{'Service.PIDFile'}) { + my ($pidfile) = grep /^\/.+\.pid$/, split /\s+/, $data->{'Service.ExecStart'}; + if (defined $pidfile) { + $data->{'Service.PIDFile'} = $pidfile; + log_command("Guessing pid file [$data->{'Service.PIDFile'}] from ExecStart [$data->{'Service.ExecStart'}]\n"); + } + } +} + +my $data = {}; +parse_file($file, $data); +for my $p (@PATHS) { + if (-d "$p/$SERVICE.wants") { + $file = "$p/$SERVICE.wants"; + parse_file("$p/$SERVICE.wants", $data); + last; + } +} + +if ($COMMAND eq 'show') { + print Data::Dumper::Dumper $data; + exit; +} + +sub pidof { + my $command = shift; + my $pids = `/usr/sbin/pidof $command`; + chomp $pids; + if ($pids ne '') { + return split /\s+/, $pids; + } + return; +} + +sub get_exec_start { + my $data = shift; + my $d = $data->{'Service.ExecStart'}; + if (not defined $d) { + warn "No ExecStart value found for [$SERVICE].\n"; + exit 3; + } + return split /\s+/, $d; +} + +sub get_pid { + my $file = shift; + if (not $file =~ m!^/!) { + $file = "$RUNNING_DIR/$file"; + } + if (-f $file) { + local * PIDFILE; + open PIDFILE, '<', $file; + my $pid = ; + close PIDFILE; + if (defined $pid) { + chomp $pid; + return $pid; + } + } +} + +sub is_running { + my $data = shift; + my $ret; + if (-f "$RUNNING_DIR/$FULL_SERVICE.oneshot") { + return 1; + } elsif (defined $data->{'Service.PIDFile'}) { + my $pid = get_pid($data->{'Service.PIDFile'}); + if (defined $pid) { + $ret = kill 0, $pid; + } + } else { + my $path = get_pid("$FULL_SERVICE.name"); + if (defined $path) { + if (pidof($path)) { + $ret = 1; + } + } else { + my $pid = get_pid("$FULL_SERVICE.pid"); + if (defined $pid) { + $ret = kill 0, $pid; + } + } + } + return $ret; +} +if ($COMMAND eq 'is-active' or $COMMAND eq 'status') { + if (is_running($data)) { + print "active\n"; + exit; + } + print "inactive\n"; + exit 3; +} + +sub exec_stop_pre { + my ($data) = @_; + if (defined $data->{'Socket.ExecStopPre'}) { + for my $x (@{ $data->{'Socket.ExecStopPre'} }) { + log_command("Running stop pre [$x]\n"); + system $x; + } + } +} +sub exec_stop_post { + my ($data) = @_; + if (defined $data->{'Service.ExecStopPost'}) { + for my $x (@{ $data->{'Service.ExecStopPost'} }) { + log_command("Running stop post [$x]\n"); + system $x; + } + } +} + +if ($TYPE eq 'target' and not defined $data->{'Unit.Wants'} and not defined $data->{'Unit.Requires'}) { + warn "No Unit.Wants/.wants/Unit.Requires/.requires list for target [$SERVICE]\n"; + exit 8; +} + +if ($COMMAND eq 'restart') { + if ($TYPE ne 'service' or is_running($data)) { + system $0, 'stop', $FULL_SERVICE; + } + system $0, 'start', $FULL_SERVICE; + exit; +} + +sub pids_went_away_timeout { + my $timeout = shift; + my $start = time; + while (kill 0, @_) { + if (time - $start > $timeout) { + log_command(sprintf(" ** pid(s) [%s] not killed within %d s\n", join(', ', @_), $timeout)); + return 0; + } + sleep 0.1; + } + log_command(sprintf(" ** pid(s) [%s] got killed after %.2f s\n", join(', ', @_), time - $start)); + return 1; +} + +sub stop_pids { + my @pids = @_; + kill 15, @pids; + if (not pids_went_away_timeout(5, @pids)) { + kill 9, @pids; + if (not pids_went_away_timeout(5, @pids)) { + log_command("Failed to kill [@pids] even with 9\n"); + warn "Failed to kill [@pids].\n"; + return 1; + } + log_command("Killed [@pids] with 9\n"); + } else { + log_command("Killed [@pids] with 15\n"); + } + return 0; +} + +if ($COMMAND eq 'stop') { + my $mainpid; + if (defined $data->{'Service.PIDFile'}) { + $mainpid = get_pid($data->{'Service.PIDFile'}); + } else { + $mainpid = get_pid("$FULL_SERVICE.pid"); + } + if (defined $data->{'Service.ExecStop'}) { + for my $x (@{ $data->{'Service.ExecStop'} }) { + my $runit = 1; + if (defined $mainpid) { + $x =~ s!\$MAINPID\b|\$\{MAINPID\}!$mainpid!g; + } elsif ($x =~ /\$MAINPID\b|\$\{MAINPID\}/) { + $runit = 0; + log_command("Service [$FULL_SERVICE] would like to stop via ExecStop [$x] but we have no pid file, skipping.\n"); + } + if ($runit) { + log_command("Running stop [$x]\n"); + system $x; + sleep 1; + } + } + } + my $ret = undef; + if ($TYPE eq 'target') { + # noop + } elsif (defined $data->{'Service.Type'} and $data->{'Service.Type'} eq 'oneshot') { + # noop + } elsif ($TYPE eq 'socket') { + my $pids = `/usr/sbin/fuser $data->{'Socket.ListenStream'} 2> /dev/null`; + if (defined $pids) { + chomp $pids; + $pids =~ s/^\s+//; + my @pids = split /\s+/, $pids; + if (@pids) { + exec_stop_pre($data); + log_command("Will kill [@pids] as fuser [$data->{'Socket.ListenStream'}] of [$FULL_SERVICE]\n"); + $ret = stop_pids(@pids); + } + } + } elsif (defined $data->{'Service.PIDFile'}) { + my $pid = get_pid($data->{'Service.PIDFile'}); + if (defined $pid) { + log_command("Will kill [$pid] found in Service.PIDFile of [$FULL_SERVICE]\n"); + $ret = stop_pids($pid); + } + } else { + my $path = get_pid("$FULL_SERVICE.name"); + if (defined $path) { + if (my @pids = pidof($path)) { + log_command("Will kill [@pids] as pidof [$path] found in [$FULL_SERVICE.name]\n"); + $ret = stop_pids(@pids); + } else { + warn "No pidof for [$path] found in [$FULL_SERVICE.name].\n"; + } + } else { + my $pid = get_pid("$FULL_SERVICE.pid"); + if (defined $pid) { + log_command("Will kill [$pid] found in [$FULL_SERVICE.pid]\n"); + $ret = stop_pids($pid); + } else { + warn "No pid and no name for [$FULL_SERVICE].\n"; + } + } + } + exec_stop_post($data); + unlink "$RUNNING_DIR/$FULL_SERVICE.pid", "$RUNNING_DIR/$FULL_SERVICE.name", "$RUNNING_DIR/$FULL_SERVICE.oneshot"; + if (defined $data->{'Unit.Wants'}) { + for my $x (@{ $data->{'Unit.Wants'} }) { + system $0, 'stop', $x; + } + } + if (defined $data->{'Unit.Requires'}) { + for my $x (@{ $data->{'Unit.Requires'} }) { + system $0, 'stop', $x; + } + } + exit ( defined $ret ? $ret : 0 ); +} + +sub add_runuser { + my ($data, $cmd) = @_; + if (defined $data->{'Service.User'}) { + unshift @$cmd, '-u', $data->{'Service.User'}, '--'; + if (defined $data->{'Service.Group'}) { + unshift @$cmd, '-g', $data->{'Service.Group'}; + } + unshift @$cmd, '/usr/sbin/runuser'; + } +} + +if ($COMMAND eq 'start') { + my (@starting_stack, %starting_stack); + if (defined $ENV{_SYSTEMCTL_LITE_STARTING}) { + @starting_stack = split /:/, $ENV{_SYSTEMCTL_LITE_STARTING}; + @starting_stack{@starting_stack} = (); + $ENV{_SYSTEMCTL_LITE_STARTING} .= ":$FULL_SERVICE"; + } else { + $ENV{_SYSTEMCTL_LITE_STARTING} = $FULL_SERVICE; + } + + if ($TYPE eq 'service' and is_running($data)) { + log_command("Service [$FULL_SERVICE] already found running, not starting again.\n"); + exit; + } + if (defined $data->{'Service.PIDFile'}) { + log_command("Service [$FULL_SERVICE] defines PIDFile [$data->{'Service.PIDFile'}], unlinking it before start\n"); + unlink $data->{'Service.PIDFile'}; + } + if (defined $data->{'Unit.Wants'}) { + for my $x (@{ $data->{'Unit.Wants'} }) { + if (exists $starting_stack{$x}) { + log_command("Skipping start of [$x], we are already in the process of starting it.\n"); + next; + } + my @cmd = ($0, 'start', $x); + log_command("Running [@cmd] for Unit.Wants of [$FULL_SERVICE]\n"); + system @cmd; + } + } + if (defined $data->{'Unit.Requires'}) { + for my $x (@{ $data->{'Unit.Requires'} }) { + if (exists $starting_stack{$x}) { + log_command("Skipping start of [$x], we are already in the process of starting it.\n"); + next; + } + my @cmd = ($0, 'start', $x); + log_command("Running [@cmd] for Unit.Requires of [$FULL_SERVICE]\n"); + if (system @cmd) { + log_command("Failed to start [$x], aborting start\n"); + exit 1; + } + } + } + if (defined $data->{'Service.PIDFile'} and is_running($data)) { + log_command("Service [$FULL_SERVICE] defines PIDFile [$data->{'Service.PIDFile'}] and it seems to have already started, not starting again.\n"); + exit; + } + + if ($TYPE eq 'target') { + exit; + } + if ($TYPE eq 'socket' and -S $data->{'Socket.ListenStream'}) { + my $out = `/usr/sbin/fuser $data->{'Socket.ListenStream'} 2> /dev/null`; + if (defined $out and $out ne '') { + log_command("Service [$FULL_SERVICE] already found active on socket [$data->{'Socket.ListenStream'}], not starting again.\n"); + exit; + } + } + if (defined $data->{'Service.Type'} and $data->{'Service.Type'} eq 'dbus') { + my @cmd = ($0, 'start', 'dbus.socket'); + log_command("Running [@cmd] for Service.Type dbus\n"); + system @cmd; + } + if (defined $data->{'Service.ExecStartPre'}) { + for my $x (@{ $data->{'Service.ExecStartPre'} }) { + my $can_fail = 0; + if ($x =~ s/^-//) { + $can_fail = 1; + } + my @cmd = split /\s+/, $x; + if (not $data->{'Service.PermissionsStartOnly'} or $data->{'Service.PermissionsStartOnly'} =~ /^(false|0)$/i) { + add_runuser($data, \@cmd); + } + no warnings 'uninitialized'; + log_command("Running start pre [@cmd]\n"); + if (system @cmd and not $can_fail) { + exit 1; + } + } + } + my @paths; + if ($TYPE eq 'socket') { + my $service = $SERVICE; + if (defined $data->{'Socket.Accept'} and $data->{'Socket.Accept'} eq 'true') { + $service =~ s/\.socket$/\@.service/; + @paths = ( '/bin/systemctl-socket-daemon', $data->{'Socket.ListenStream'}, $data->{'Socket.SocketMode'} // '0666', $service ); + } else { + $service =~ s/\.socket$/\.service/; + my $ret = system "$0 is-active $service > /dev/null"; + if (($ret >> 8) == 0) { + log_command("Service [$service] is already running for [$SERVICE]\n"); + exit; + } + @paths = ( $0, 'start', $service ); + } + } else { + @paths = get_exec_start($data); + } + my $first_path = $paths[0]; + + my $ENV = ''; + if (exists $data->{'Service.EnvironmentFile'}) { + for my $e (@{ $data->{'Service.EnvironmentFile'} }) { + my $error_fail = 1; + if ($e =~ s/^-//) { + $error_fail = 0; + } + my $fh = new IO::File($e); + if (not defined $fh) { + if ($error_fail) { + warn "Error reading EnvironmentFile [$e]: $!\n"; + exit 5; + } + } else { + while (my $line = <$fh>) { + chomp $line; + next if $line =~ /^\s*(#|$)/; + $ENV .= "export $line; "; + } + $fh->close(); + } + } + } + if (defined $data->{'Service.Environment'}) { + my $x = $data->{'Service.Environment'}; + $x =~ s/^"(.*)"$/$1/; + $ENV .= "export $x; "; + } + add_runuser($data, \@paths); + log_command("Running [$ENV@paths]\n"); + if (defined $data->{'Service.Type'} and $data->{'Service.Type'} eq 'oneshot') { + system "$ENV@paths"; + if (defined $data->{'Service.RemainAfterExit'} and $data->{'Service.RemainAfterExit'} eq 'yes') { + local * PIDFILE; + open PIDFILE, '>', "$RUNNING_DIR/$FULL_SERVICE.oneshot"; + close PIDFILE; + } + exit; + } + my $pid = fork(); + die "Failed to fork for [@_]\n" if not defined $pid; + if ($pid == 0) { + open(STDOUT, '>>', '/var/log/systemctl.log'); + open(STDERR, '>>', '/var/log/systemctl.log'); + open(STDIN, '<', '/dev/null'); + POSIX::setsid(); + exec "$ENV@paths"; + } + if (defined $data->{'Service.PIDFile'}) { + log_command("Service [$FULL_SERVICE] defines PIDFile [$data->{'Service.PIDFile'}], not marking pid\n"); + unlink "$RUNNING_DIR/$FULL_SERVICE.pid"; + my $i = 0; + while (not -f $data->{'Service.PIDFile'}) { + sleep 0.2; + $i++; + if ($i > 250) { + log_command("Starting [$FULL_SERVICE] did not create PIDFile [$data->{'Service.PIDFile'}]\n"); + exit 10; + } + } + my $pid = get_pid($data->{'Service.PIDFile'}); + if (not defined $pid or not kill 0, $pid) { + log_command("Starting [$FULL_SERVICE] created PIDFile [$data->{'Service.PIDFile'}] but the process is not running\n"); + exit 11; + } + } else { + local * PIDFILE; + open PIDFILE, '>', "$RUNNING_DIR/$FULL_SERVICE.pid"; + print PIDFILE "$pid\n"; + close PIDFILE; + log_command("Marked pid [$pid] for [$FULL_SERVICE]\n"); + sleep 1; + if (defined $data->{'Service.Type'} and $data->{'Service.Type'} ne 'simple' and $data->{'Service.Type'} ne 'notify' and pidof($first_path)) { + local * PIDFILE; + open PIDFILE, '>', "$RUNNING_DIR/$FULL_SERVICE.name"; + print PIDFILE "$first_path\n"; + close PIDFILE; + log_command("Marked process name [$first_path] for [$FULL_SERVICE]\n"); + } + } + if (defined $data->{'Service.Type'} + and $data->{'Service.Type'} eq 'dbus' + and defined $data->{'Service.BusName'}) { + my $busname = $data->{'Service.BusName'}; + my $objectpath = $busname; + $objectpath =~ s!^|\.!/!g; + for (0 .. 10) { + system "/usr/bin/dbus-send --system --type=method_call --print-reply --dest=$busname $objectpath org.freedesktop.DBus.Introspectable.Introspect > /dev/null"; + exit if ($? >> 8) == 0; + sleep 1; + } + exit 9; + } + exit; +} + +if ($COMMAND eq 'is-enabled') { + if (-e "$ENABLED_DIR/$FULL_SERVICE") { + print "enabled\n"; + exit 0; + } + print "disabled\n"; + exit 1; +} + +if ($COMMAND eq 'enable') { + if (not -d $ENABLED_DIR) { + mkdir $ENABLED_DIR; + } + local * FILE; + open FILE, '>', "$ENABLED_DIR/$FULL_SERVICE"; + exit; +} + +if ($COMMAND eq 'disable') { + unlink "$ENABLED_DIR/$FULL_SERVICE"; + exit; +} + +die "Unknown command [$COMMAND].\n"; + +1; + diff --git a/99-ci-maipo/systemctl-socket-daemon b/99-ci-maipo/systemctl-socket-daemon new file mode 100644 index 0000000..5250cad --- /dev/null +++ b/99-ci-maipo/systemctl-socket-daemon @@ -0,0 +1,67 @@ +#!/usr/bin/perl + +# Copyright 2014 Jan Pazdziora +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +use strict; +use warnings FATAL => 'all'; + +use IO::Socket::UNIX (); +use Socket (); +use POSIX (); + +sub daemonize { + open(STDERR, '>>', '/var/log/systemctl-socket-daemon.log') || die "can't write log: $!"; + open(STDOUT, '>&STDERR') || die "can't write stdout to log: $!"; + chdir("/") || die "can't chdir to /: $!"; + open(STDIN, '<', '/dev/null') || die "can't read /dev/null: $!"; + defined(my $pid = fork()) || die "can't fork: $!"; + exit if $pid; # non-zero now means I am the parent + (POSIX::setsid() != -1) || die "Can't start a new session: $!"; +} + +my ($socket_path, $socket_mode, $service) = @ARGV; +if (not defined $socket_path or not defined $service) { + die "Usage: $0 /path/to/unix/socket service-to-run\n"; +} +if (-e $socket_path) { + warn "Path [$socket_path] already exists, removing\n"; + unlink $socket_path; +} + +my $service_data = `/bin/systemctl show $service 2>&1`; +if ($?) { + die "Failed to find service [$service]:\n$service_data"; +} + +my $socket = new IO::Socket::UNIX( + Type => Socket::SOCK_STREAM, + Local => $socket_path, + Listen => Socket::SOMAXCONN +) or die "socket: $!\n"; +chmod oct($socket_mode), $socket_path; + +daemonize(); + +while (1) { + next unless my $connection = $socket->accept; + my $pid = fork(); + if ($pid == 0) { + *STDIN = $connection; + *STDOUT = $connection; + exec '/bin/systemctl', 'start', $service; + die "exec should have never reached here\n"; + } +} + diff --git a/99-ci-maipo/vimrc b/99-ci-maipo/vimrc new file mode 100644 index 0000000..224ffaa --- /dev/null +++ b/99-ci-maipo/vimrc @@ -0,0 +1,8 @@ +set shiftwidth=4 +set tabstop=4 +set expandtab + +autocmd BufNewFile,BufRead /srv/cyrus-imapd.git/*/*.{c,h} set tabstop=8 softtabstop=4 shiftwidth=4 list listchars=tab:>. noexpandtab +autocmd BufNewFile,BufRead /srv/cyrus-imapd.git/cunit/cunit.pl set tabstop=8 softtabstop=4 shiftwidth=4 list listchars=tab:>. noexpandtab +autocmd BufNewFile,BufRead /srv/cyrus-imapd.git/configure.ac set tabstop=8 shiftwidth=8 noexpandtab +