Skip to content

Commit

Permalink
Merge pull request #4522 from elliefm/v39/proc-info-for-daemons
Browse files Browse the repository at this point in the history
register DAEMON and EVENTS processes so cyr_info proc can list them
  • Loading branch information
elliefm authored Jun 28, 2023
2 parents d4c7b44 + 934a332 commit 80e1857
Show file tree
Hide file tree
Showing 32 changed files with 1,095 additions and 160 deletions.
7 changes: 4 additions & 3 deletions Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -642,6 +642,7 @@ cunit_TESTS = \
cunit/msgid.testc \
cunit/parseaddr.testc \
cunit/parse.testc \
cunit/proc.testc \
cunit/procinfo.testc \
cunit/prot.testc \
cunit/ptrarray.testc \
Expand Down Expand Up @@ -793,6 +794,7 @@ include_HEADERS = \
lib/murmurhash2.h \
lib/nonblock.h \
lib/parseaddr.h \
lib/proc.h \
lib/procinfo.h \
lib/retry.h \
lib/rfc822tok.h \
Expand Down Expand Up @@ -1065,8 +1067,6 @@ imap_libcyrus_imap_la_SOURCES = \
imap/notify.h \
imap/partlist.c \
imap/partlist.h \
imap/proc.c \
imap/proc.h \
imap/prometheus.c \
imap/prometheus.h \
imap/protocol.h \
Expand All @@ -1087,7 +1087,6 @@ imap_libcyrus_imap_la_SOURCES = \
imap/search_sort.h \
imap/seen.h \
imap/seen_db.c \
imap/setproctitle.c \
imap/sievedir.c \
imap/sievedir.h \
imap/smtpclient.c \
Expand Down Expand Up @@ -1573,7 +1572,9 @@ lib_libcyrus_min_la_SOURCES = \
lib/hashu64.c \
lib/libconfig.c \
lib/mpool.c \
lib/proc.c \
lib/retry.c \
lib/setproctitle.c \
lib/smallarrayu64.c \
lib/strarray.c \
lib/strhash.c \
Expand Down
22 changes: 15 additions & 7 deletions backup/backupd.c
Original file line number Diff line number Diff line change
Expand Up @@ -56,14 +56,14 @@
#include "lib/bsearch.h"
#include "lib/imparse.h"
#include "lib/map.h"
#include "lib/proc.h"
#include "lib/signals.h"
#include "lib/strarray.h"
#include "lib/util.h"
#include "lib/xmalloc.h"

#include "imap/global.h"
#include "imap/imap_err.h"
#include "imap/proc.h"
#include "imap/sync_support.h"
#include "imap/telemetry.h"
#include "imap/tls.h"
Expand All @@ -84,6 +84,7 @@ static sasl_conn_t *backupd_saslconn = NULL;
static int backupd_starttls_done = 0;
static int backupd_compress_done = 0;
static int backupd_logfd = -1;
static struct proc_handle *proc_handle = NULL;

struct open_backup {
char *name;
Expand Down Expand Up @@ -159,7 +160,7 @@ EXPORTED void fatal(const char* s, int code)

if (recurse_code) {
/* We were called recursively. Just give up */
proc_cleanup();
proc_cleanup(&proc_handle);
exit(recurse_code);
}
recurse_code = code;
Expand All @@ -184,7 +185,7 @@ EXPORTED int service_init(int argc __attribute__((unused)),
{
// FIXME should this be calling fatal? fatal exits directly
if (geteuid() == 0) fatal("must run as the Cyrus user", EX_USAGE);
setproctitle_init(argc, argv, envp);
proc_settitle_init(argc, argv, envp);

/* set signal handlers */
signals_set_shutdown(&shut_down);
Expand Down Expand Up @@ -222,7 +223,7 @@ EXPORTED int service_main(int argc __attribute__((unused)),
{
const char *localip, *remoteip;
sasl_security_properties_t *secprops = NULL;
int timeout;
int r, timeout;

signals_poll();

Expand Down Expand Up @@ -268,7 +269,10 @@ EXPORTED int service_main(int argc __attribute__((unused)),
tcp_disable_nagle(1); /* XXX magic fd */
}

proc_register(config_ident, backupd_clienthost, NULL, NULL, NULL);
r = proc_register(&proc_handle, 0,
config_ident, backupd_clienthost, NULL, NULL, NULL);
if (r) fatal("unable to register process", EX_IOERR);
proc_settitle(config_ident, backupd_clienthost, NULL, NULL, NULL);

/* Set inactivity timer */
timeout = config_getduration(IMAPOPT_SYNC_TIMEOUT, 's');
Expand All @@ -295,7 +299,7 @@ static void backupd_reset(void)
{
open_backups_list_close(&backupd_open_backups, 0);

proc_cleanup();
proc_cleanup(&proc_handle);

if (backupd_in) {
prot_NONBLOCK(backupd_in);
Expand Down Expand Up @@ -823,7 +827,11 @@ static void cmd_authenticate(char *mech, char *resp)
}

backupd_userid = xstrdup((const char *) val);
proc_register(config_ident, backupd_clienthost, backupd_userid, NULL, NULL);
r = proc_register(&proc_handle, 0,
config_ident, backupd_clienthost, backupd_userid,
NULL, NULL);
if (r) fatal("unable to register process", EX_IOERR);
proc_settitle(config_ident, backupd_clienthost, backupd_userid, NULL, NULL);

syslog(LOG_NOTICE, "login: %s %s %s%s %s", backupd_clienthost, backupd_userid,
mech, backupd_starttls_done ? "+TLS" : "", "User logged in");
Expand Down
6 changes: 3 additions & 3 deletions cassandane/Cassandane/Cyrus/Annotator.pm
Original file line number Diff line number Diff line change
Expand Up @@ -78,14 +78,14 @@ sub start_my_instances
{
my ($self) = @_;

$self->{instance}->add_generic_daemon(
$self->{instance}->add_generic_listener(
name => 'annotator',
port => $self->{instance}->{config}->get('annotation_callout'),
argv => sub {
my ($daemon) = @_;
my ($listener) = @_;
return (
abs_path('utils/annotator.pl'),
'--port', $daemon->port(),
'--port', $listener->port(),
'--pidfile', '@basedir@/run/annotator.pid',
);
});
Expand Down
185 changes: 185 additions & 0 deletions cassandane/Cassandane/Cyrus/Info.pm
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,9 @@
package Cassandane::Cyrus::Info;
use strict;
use warnings;
use Cwd qw(realpath);
use Data::Dumper;
use Date::Format qw(time2str);

use lib '.';
use base qw(Cassandane::Cyrus::TestCase);
Expand Down Expand Up @@ -86,6 +88,11 @@ sub run_cyr_info
my @res = readline(RESULTS);
close RESULTS;

if ($args[0] eq 'proc') {
# if we see our fakesaslauthd, no we didn't
@res = grep { $_ !~ m/\bfakesaslauthd\b/ } @res;
}

return @res;
}

Expand Down Expand Up @@ -211,4 +218,182 @@ sub test_info_lint_partitions
);
}

sub test_proc_services
{
my ($self) = @_;

# no clients => no service daemons => no processes
my @output = $self->run_cyr_info('proc');
$self->assert_num_equals(0, scalar @output);

# master spawns service processes when clients connect to them
my $imap_svc = $self->{instance}->get_service('imap');
my @clients;
foreach (1..5) {
# five concurrent connections for a single user is normal,
# e.g. thunderbird does this
my $store = $imap_svc->create_store(username => 'cassandane');
my $imaptalk = $store->get_client();
push @clients, $imaptalk if $imaptalk;
}

# better have got some clients from that!
$self->assert_num_gte(1, scalar @clients);

# five clients => five service daemons => five processes
@output = $self->run_cyr_info('proc');
$self->assert_num_equals(scalar @clients, scalar @output);

# log clients out one at a time, expect proc count to decrease
while (scalar @clients) {
my $old = shift @clients;
$old->logout();

@output = $self->run_cyr_info('proc');
$self->assert_num_equals(scalar @clients, scalar @output);
}
}

sub test_proc_starts
:NoStartInstances
{
my ($self) = @_;

# we used to recommend starting idled from START, and it will
# still work like that, so using it here saves me mocking something
$self->{instance}->add_start(name => 'idled',
argv => [ 'idled' ]);
$self->{instance}->start();

# entries listed in START run to completion before master fully
# starts up. if they fork themselves and hang around (like idled
# does) then that's their business, but master can't and doesn't
# track them
my @output = $self->run_cyr_info('proc');

$self->assert_num_equals(0, scalar @output);
}

sub test_proc_periodic_events_slow
:NoStartInstances
{
my ($self) = @_;

my $sleeper_time = 10; # seconds

# periodic events first fire immediately at startup, and then every
# 'period' minutes thereafter. the fastest we can schedule them is
# every 1 minute, so this test must run for at least several real
# minutes
$self->{instance}->add_event(
name => 'sleeper',
argv => [ realpath('utils/sleeper'), $sleeper_time ],
period => 1,
);
$self->{instance}->start();

sleep 2; # offset our checks a little to avoid races

# observe for three cycles
my $observations = 3;
while ($observations > 0) {
# event should have fired and be running
my @output = $self->run_cyr_info('proc');
$self->assert_num_equals(1, scalar @output);

# wait for it to finish and check again
sleep $sleeper_time;
@output = $self->run_cyr_info('proc');
$self->assert_num_equals(0, scalar @output);

# skip final wait if we're done
$observations--;
last if $observations == 0;

# wait until next period
sleep 60 - $sleeper_time;
}
}

sub test_proc_scheduled_events
:NoStartInstances
{
my ($self) = @_;

my $sleeper_time = 10;

# schedule an event to fire at the next minute boundary that is at
# least ten seconds away
my $at = time + 70;
$at -= ($at % 60);
my $at_hm = time2str('%H%M', $at);
xlog $self, "scheduling event to run at $at_hm ($at)";
$self->{instance}->add_event(
name => 'sleeper',
argv => [ realpath('utils/sleeper'), $sleeper_time ],
at => $at_hm,
);
$self->{instance}->start();

# event process should not be running at startup
my @output = $self->run_cyr_info('proc');
$self->assert_num_equals(0, scalar @output);

# should be running at the scheduled time (with a little slop)
sleep 2 + $at - time;
@output = $self->run_cyr_info('proc');
$self->assert_num_equals(1, scalar @output);

# should not be running after we expect it to have finished
sleep $sleeper_time;
@output = $self->run_cyr_info('proc');
$self->assert_num_equals(0, scalar @output);
}

sub test_proc_daemons
:NoStartInstances
{
my ($self) = @_;

my $sleeper_time = 10; # seconds
my $daemons = 3;

for my $i (1 .. $daemons) {
# you wouldn't usually run a daemon that exits and needs to be
# restarted every ten seconds, but it's useful for testing
# that cyr_info proc notices the pid changing
$self->{instance}->add_daemon(
name => "sleeper$i",
argv => [ realpath('utils/sleeper'), $sleeper_time ],
);
}
$self->{instance}->start();

sleep 2; # offset our checks a little to avoid races

my $observations = 3;
my %lastpid = map {; "sleeper$_" => 0 } (1 .. $daemons);
while ($observations > 0) {
my @output = $self->run_cyr_info('proc');

# always exactly one process per daemon
$self->assert_num_equals($daemons, scalar @output);

# expect a new pid for each daemon each time
foreach my $line (@output) {
my ($pid, $servicename, $host, $user, $mailbox, $cmd)
= split /\s/, $line, 6;
$self->assert_num_not_equals($lastpid{$servicename}, $pid);
$lastpid{$servicename} = $pid;
}

# skip final wait if we're done
$observations--;
last if $observations == 0;

# wait for next restart
sleep $sleeper_time;
}
}

1;
6 changes: 3 additions & 3 deletions cassandane/Cassandane/Cyrus/Master.pm
Original file line number Diff line number Diff line change
Expand Up @@ -845,7 +845,7 @@ sub test_maxforkrate
}, $self->lemming_census());
}

sub XXXtest_periodic_event
sub test_periodic_event_slow
{
my ($self) = @_;

Expand All @@ -860,8 +860,8 @@ sub XXXtest_periodic_event

xlog $self, "periodic events run immediately";

xlog $self, "waiting 5 mins for events to fire";
sleep(5*60);
xlog $self, "waiting 5 mins for events to fire, plus some slop";
sleep(5*60 + 5);

$self->assert_deep_equals({
B => { live => 0, dead => 6 },
Expand Down
3 changes: 2 additions & 1 deletion cassandane/Cassandane/Cyrus/TestCase.pm
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ use base qw(Cassandane::Unit::TestCase);
use Cassandane::Util::Log;
use Cassandane::Util::Words;
use Cassandane::Generator;
use Cassandane::GenericListener;
use Cassandane::MessageStoreFactory;
use Cassandane::Instance;
use Cassandane::PortManager;
Expand Down Expand Up @@ -1026,7 +1027,7 @@ sub post_tear_down
}

die "Found some stray processes"
if (Cassandane::GenericDaemon::kill_processes_on_ports(
if (Cassandane::GenericListener::kill_processes_on_ports(
Cassandane::PortManager::free_all()));
}

Expand Down
Loading

0 comments on commit 80e1857

Please sign in to comment.