Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

register DAEMON and EVENTS processes so cyr_info proc can list them #4522

Merged
merged 16 commits into from
Jun 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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