Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F117749734
master.c
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
24 KB
Referenced Files
None
Subscribers
None
master.c
View Options
/* master.c -- IMAP master process to handle recovery, checkpointing, spawning
*
* Copyright (c) 2000 Carnegie Mellon University. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "Carnegie Mellon University" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For permission or any other legal
* details, please contact
* Office of Technology Transfer
* Carnegie Mellon University
* 5000 Forbes Avenue
* Pittsburgh, PA 15213-3890
* (412) 268-4387, fax: (412) 268-7395
* tech-transfer@andrew.cmu.edu
*
* 4. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by Computing Services
* at Carnegie Mellon University (http://www.cmu.edu/computing/)."
*
* CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
* THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
* FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/* $Id: master.c,v 1.27 2000/12/27 19:18:10 ken3 Exp $ */
#include
<config.h>
#include
<stdio.h>
#include
<stdlib.h>
#include
<string.h>
#include
<sys/time.h>
#include
<sys/types.h>
#include
<sys/wait.h>
#ifdef HAVE_UNISTD_H
#include
<unistd.h>
#endif
#ifdef HAVE_SYS_RESOURCE_H
#include
<sys/resource.h>
#endif
#include
<fcntl.h>
#include
<signal.h>
#include
<pwd.h>
#include
<sys/param.h>
#include
<sys/stat.h>
#include
<syslog.h>
#include
<netdb.h>
#include
<ctype.h>
#include
<sys/socket.h>
#include
<netinet/in.h>
#include
<sys/un.h>
#include
<arpa/inet.h>
#include
<sysexits.h>
#include
<errno.h>
#ifndef INADDR_NONE
#define INADDR_NONE 0xffffffff
#endif
#ifndef INADDR_ANY
#define INADDR_ANY 0x00000000
#endif
#ifdef HAVE_UCDSNMP
#include
<ucd-snmp/ucd-snmp-config.h>
#include
<ucd-snmp/ucd-snmp-includes.h>
#include
<ucd-snmp/ucd-snmp-agent-includes.h>
#include
"cyrusMasterMIB.h"
#endif
#include
"masterconf.h"
#include
"master.h"
#include
"service.h"
#define SERVICE_PATH (CYRUS_PATH "/bin")
enum
{
become_cyrus_early
=
1
,
child_table_size
=
10000
,
child_table_inc
=
100
};
static
int
verbose
=
0
;
static
int
listen_queue_backlog
=
10
;
struct
service
*
Services
=
NULL
;
int
allocservices
=
0
;
int
nservices
=
0
;
struct
recover
{
char
*
name
;
char
*
const
*
exec
;
};
struct
event
{
char
*
name
;
time_t
mark
;
time_t
period
;
char
*
const
*
exec
;
struct
event
*
next
;
};
static
struct
event
*
schedule
=
NULL
;
struct
centry
{
pid_t
pid
;
struct
service
*
s
;
struct
centry
*
next
;
};
static
struct
centry
*
ctable
[
child_table_size
];
static
struct
centry
*
cfreelist
;
void
fatal
(
char
*
msg
,
int
code
)
{
syslog
(
LOG_CRIT
,
"%s"
,
msg
);
syslog
(
LOG_NOTICE
,
"exiting"
);
exit
(
code
);
}
int
become_cyrus
(
void
)
{
struct
passwd
*
p
;
static
int
uid
=
0
;
if
(
uid
)
return
setuid
(
uid
);
p
=
getpwnam
(
CYRUS_USER
);
if
(
p
==
NULL
)
{
syslog
(
LOG_ERR
,
"no entry in /etc/passwd for %s"
,
CYRUS_USER
);
return
-1
;
}
uid
=
p
->
pw_uid
;
return
setuid
(
uid
);
}
void
get_prog
(
char
*
path
,
char
*
const
*
cmd
)
{
if
(
cmd
[
0
][
0
]
==
'/'
)
strcpy
(
path
,
cmd
[
0
]);
else
sprintf
(
path
,
"%s/%s"
,
SERVICE_PATH
,
cmd
[
0
]);
}
void
get_statsock
(
int
filedes
[
2
])
{
int
r
,
fdflags
;
r
=
pipe
(
filedes
);
if
(
r
!=
0
)
{
fatal
(
"couldn't create status socket: %m"
,
1
);
}
/* we don't want the master blocking on reads */
fdflags
=
fcntl
(
filedes
[
0
],
F_GETFL
,
0
);
if
(
fdflags
!=
-1
)
fdflags
=
fcntl
(
filedes
[
0
],
F_SETFL
,
fdflags
|
O_NONBLOCK
);
if
(
fdflags
==
-1
)
{
fatal
(
"unable to set non-blocking: %m"
,
1
);
}
/* we don't want the services to be able to read from it */
fdflags
=
fcntl
(
filedes
[
0
],
F_GETFD
,
0
);
if
(
fdflags
!=
-1
)
fdflags
=
fcntl
(
filedes
[
0
],
F_SETFD
,
fdflags
|
FD_CLOEXEC
);
if
(
fdflags
==
-1
)
{
fatal
(
"unable to set close-on-exec: %m"
,
1
);
}
}
/* return a new 'centry', either from the freelist or by malloc'ing it */
static
struct
centry
*
get_centry
(
void
)
{
struct
centry
*
t
;
if
(
!
cfreelist
)
{
/* create child_table_inc more and add them to the freelist */
struct
centry
*
n
;
int
i
;
n
=
malloc
(
child_table_inc
*
sizeof
(
struct
centry
));
cfreelist
=
n
;
for
(
i
=
0
;
i
<
child_table_inc
-
1
;
i
++
)
{
n
[
i
].
next
=
n
+
(
i
+
1
);
}
/* i == child_table_inc - 1, last item in block */
n
[
i
].
next
=
NULL
;
}
t
=
cfreelist
;
cfreelist
=
cfreelist
->
next
;
return
t
;
}
/* see if 'listen' parameter has both hostname and port, or just port */
char
*
parse_listen
(
char
*
listen
)
{
char
*
cp
;
char
*
port
=
NULL
;
if
((
cp
=
strrchr
(
listen
,
']'
))
!=
NULL
)
{
/* ":port" after closing bracket for IP address? */
if
(
*
cp
++
!=
'\0'
&&
*
cp
==
':'
)
{
*
cp
++
=
'\0'
;
if
(
*
cp
!=
'\0'
)
{
port
=
cp
;
}
}
}
else
if
((
cp
=
strrchr
(
listen
,
':'
))
!=
NULL
)
{
/* ":port" after hostname? */
*
cp
++
=
'\0'
;
if
(
*
cp
!=
'\0'
)
{
port
=
cp
;
}
}
return
port
;
}
/* set sin_port accordingly. return of 0 indicates failure. */
int
resolve_port
(
char
*
port
,
struct
service
*
s
,
struct
sockaddr_in
*
sin
)
{
struct
servent
*
serv
;
serv
=
getservbyname
(
port
,
s
->
proto
);
if
(
serv
)
{
sin
->
sin_port
=
serv
->
s_port
;
}
else
{
sin
->
sin_port
=
htons
(
atoi
(
port
));
if
(
sin
->
sin_port
==
0
)
{
syslog
(
LOG_INFO
,
"no service '%s' in /etc/services, "
"disabling %s"
,
port
,
s
->
name
);
s
->
exec
=
NULL
;
return
0
;
}
}
return
1
;
}
/* set sin_addr accordingly. return of 0 indicates failure. */
int
resolve_host
(
struct
service
*
s
,
struct
sockaddr_in
*
sin
)
{
struct
hostent
*
hp
;
char
*
cp
;
/* do we have a hostname, or IP number? */
/* XXX are brackets necessary, like for IPv6 later? */
if
(
*
s
->
listen
==
'['
)
{
s
->
listen
++
;
/* skip first bracket */
if
((
cp
=
strrchr
(
s
->
listen
,
']'
))
!=
NULL
)
{
*
cp
=
'\0'
;
}
}
sin
->
sin_addr
.
s_addr
=
inet_addr
(
s
->
listen
);
if
((
sin
->
sin_addr
.
s_addr
==
INADDR_NONE
)
||
(
sin
->
sin_addr
.
s_addr
==
0
))
{
/* looks like it isn't an IP address, so look up the host */
if
((
hp
=
gethostbyname
(
s
->
listen
))
==
0
)
{
syslog
(
LOG_INFO
,
"host not found: %s"
,
s
->
listen
);
s
->
exec
=
NULL
;
return
0
;
}
if
(
hp
->
h_addrtype
!=
AF_INET
)
{
syslog
(
LOG_INFO
,
"unexpected address family: %d"
,
hp
->
h_addrtype
);
s
->
exec
=
NULL
;
return
0
;
}
if
(
hp
->
h_length
!=
sizeof
(
sin
->
sin_addr
))
{
syslog
(
LOG_INFO
,
"unexpected address length %d"
,
hp
->
h_length
);
s
->
exec
=
NULL
;
return
0
;
}
memcpy
((
char
*
)
&
sin
->
sin_addr
,
hp
->
h_addr
,
hp
->
h_length
);
}
return
1
;
}
void
service_create
(
struct
service
*
s
)
{
struct
sockaddr_in
sin
;
struct
sockaddr_un
sunsock
;
struct
sockaddr
*
sa
;
mode_t
oldumask
;
int
on
=
1
,
salen
;
int
r
;
char
*
port
;
memset
(
&
sin
,
0
,
sizeof
(
sin
));
if
(
s
->
listen
[
0
]
==
'/'
)
{
/* unix socket */
sunsock
.
sun_family
=
AF_UNIX
;
strcpy
(
sunsock
.
sun_path
,
s
->
listen
);
unlink
(
s
->
listen
);
sa
=
(
struct
sockaddr
*
)
&
sunsock
;
salen
=
sizeof
(
sunsock
.
sun_family
)
+
strlen
(
sunsock
.
sun_path
)
+
1
;
s
->
socket
=
socket
(
AF_UNIX
,
SOCK_STREAM
,
0
);
}
else
{
/* inet socket */
sin
.
sin_family
=
AF_INET
;
if
((
port
=
parse_listen
(
s
->
listen
))
==
NULL
)
{
/* s->listen IS the port */
if
(
!
resolve_port
(
s
->
listen
,
s
,
&
sin
))
{
return
;
}
sin
.
sin_addr
.
s_addr
=
INADDR_ANY
;
}
else
{
/* s->listen is now just the address */
if
(
!
resolve_port
(
port
,
s
,
&
sin
))
{
return
;
}
if
(
!
resolve_host
(
s
,
&
sin
))
{
return
;
}
}
sa
=
(
struct
sockaddr
*
)
&
sin
;
salen
=
sizeof
(
sin
);
s
->
socket
=
socket
(
AF_INET
,
SOCK_STREAM
,
0
);
}
if
(
s
->
socket
<
0
)
{
syslog
(
LOG_ERR
,
"unable to create %s listener socket: %m"
,
s
->
name
);
s
->
exec
=
NULL
;
return
;
}
/* allow reuse of address */
setsockopt
(
s
->
socket
,
SOL_SOCKET
,
SO_REUSEADDR
,
(
void
*
)
&
on
,
sizeof
(
on
));
oldumask
=
umask
((
mode_t
)
0
);
/* for linux */
r
=
bind
(
s
->
socket
,
sa
,
salen
);
umask
(
oldumask
);
if
(
r
<
0
)
{
syslog
(
LOG_ERR
,
"unable to bind %s socket: %m"
,
s
->
name
);
close
(
s
->
socket
);
s
->
socket
=
0
;
s
->
exec
=
NULL
;
return
;
}
if
(
s
->
listen
[
0
]
==
'/'
)
{
/* unix socket */
/* for DUX, where this isn't the default.
(harmlessly fails on some systems) */
chmod
(
s
->
listen
,
(
mode_t
)
0777
);
}
if
(
listen
(
s
->
socket
,
listen_queue_backlog
)
<
0
)
{
syslog
(
LOG_ERR
,
"unable to listen to %s socket: %m"
,
s
->
name
);
close
(
s
->
socket
);
s
->
socket
=
0
;
s
->
exec
=
NULL
;
return
;
}
s
->
ready_workers
=
0
;
get_statsock
(
s
->
stat
);
}
void
run_startup
(
char
**
cmd
)
{
pid_t
pid
;
int
status
;
char
path
[
1024
];
switch
(
pid
=
fork
())
{
case
-1
:
syslog
(
LOG_CRIT
,
"can't fork process to run startup"
);
fatal
(
"can't run startup"
,
1
);
break
;
case
0
:
if
(
become_cyrus
()
!=
0
)
{
syslog
(
LOG_ERR
,
"can't change to the cyrus user"
);
exit
(
1
);
}
get_prog
(
path
,
cmd
);
syslog
(
LOG_DEBUG
,
"about to exec %s"
,
path
);
execv
(
path
,
cmd
);
syslog
(
LOG_ERR
,
"can't exec %s for startup: %m"
,
path
);
exit
(
1
);
default
:
if
(
waitpid
(
pid
,
&
status
,
0
)
<
0
)
{
syslog
(
LOG_ERR
,
"waitpid(): %m"
);
}
else
if
(
status
!=
0
)
{
if
(
WIFEXITED
(
status
))
{
syslog
(
LOG_ERR
,
"process %d exited, status %d
\n
"
,
pid
,
WEXITSTATUS
(
status
));
}
if
(
WIFSIGNALED
(
status
))
{
syslog
(
LOG_ERR
,
"process %d exited, signaled to death by %d
\n
"
,
pid
,
WTERMSIG
(
status
));
}
}
break
;
}
}
void
spawn_service
(
struct
service
*
s
)
{
pid_t
p
;
int
i
;
char
path
[
1024
];
int
fdflags
;
struct
centry
*
c
;
switch
(
p
=
fork
())
{
case
-1
:
syslog
(
LOG_ERR
,
"can't fork process to run checkpoint"
);
break
;
case
0
:
/* child */
if
(
become_cyrus
()
!=
0
)
{
syslog
(
LOG_ERR
,
"can't change to the cyrus user"
);
exit
(
1
);
}
get_prog
(
path
,
s
->
exec
);
if
(
dup2
(
s
->
stat
[
1
],
STATUS_FD
)
<
0
)
{
syslog
(
LOG_ERR
,
"can't duplicate status fd: %m"
);
exit
(
1
);
}
if
(
dup2
(
s
->
socket
,
LISTEN_FD
)
<
0
)
{
syslog
(
LOG_ERR
,
"can't duplicate listener fd: %m"
);
exit
(
1
);
}
fdflags
=
fcntl
(
LISTEN_FD
,
F_GETFD
,
0
);
if
(
fdflags
!=
-1
)
fdflags
=
fcntl
(
LISTEN_FD
,
F_SETFD
,
fdflags
&
~
FD_CLOEXEC
);
if
(
fdflags
==
-1
)
{
syslog
(
LOG_ERR
,
"unable to unset close on exec: %m"
);
}
fdflags
=
fcntl
(
STATUS_FD
,
F_GETFD
,
0
);
if
(
fdflags
!=
-1
)
fdflags
=
fcntl
(
STATUS_FD
,
F_SETFD
,
fdflags
&
~
FD_CLOEXEC
);
if
(
fdflags
==
-1
)
{
syslog
(
LOG_ERR
,
"unable to unset close on exec: %m"
);
}
/* close all listeners */
for
(
i
=
0
;
i
<
nservices
;
i
++
)
{
if
(
Services
[
i
].
socket
>
0
)
close
(
Services
[
i
].
socket
);
if
(
Services
[
i
].
stat
[
0
]
>
0
)
close
(
Services
[
i
].
stat
[
0
]);
if
(
Services
[
i
].
stat
[
1
]
>
0
)
close
(
Services
[
i
].
stat
[
1
]);
}
syslog
(
LOG_DEBUG
,
"about to exec %s"
,
path
);
execv
(
path
,
s
->
exec
);
syslog
(
LOG_ERR
,
"couldn't exec %s: %m"
,
path
);
default
:
/* parent */
s
->
ready_workers
++
;
s
->
nforks
++
;
s
->
nactive
++
;
/* add to child table */
c
=
get_centry
();
c
->
pid
=
p
;
c
->
s
=
s
;
c
->
next
=
ctable
[
p
%
child_table_size
];
ctable
[
p
%
child_table_size
]
=
c
;
break
;
}
}
void
schedule_event
(
struct
event
*
a
)
{
struct
event
*
ptr
;
if
(
!
schedule
||
a
->
mark
<
schedule
->
mark
)
{
a
->
next
=
schedule
;
schedule
=
a
;
return
;
}
for
(
ptr
=
schedule
;
ptr
->
next
&&
ptr
->
next
->
mark
<=
a
->
mark
;
ptr
=
ptr
->
next
)
;
/* insert a */
a
->
next
=
ptr
->
next
;
ptr
->
next
=
a
;
}
void
spawn_schedule
(
time_t
now
)
{
struct
event
*
a
,
*
b
;
char
path
[
1024
];
pid_t
p
;
struct
centry
*
c
;
a
=
NULL
;
/* update schedule accordingly */
while
(
schedule
&&
schedule
->
mark
<=
now
)
{
/* delete from schedule, insert into a */
struct
event
*
ptr
=
schedule
;
/* delete */
schedule
=
schedule
->
next
;
/* insert */
ptr
->
next
=
a
;
a
=
ptr
;
}
/* run all events */
while
(
a
&&
a
!=
schedule
)
{
switch
(
p
=
fork
())
{
case
-1
:
syslog
(
LOG_CRIT
,
"can't fork process to run event %s"
);
break
;
case
0
:
if
(
become_cyrus
()
!=
0
)
{
syslog
(
LOG_ERR
,
"can't change to the cyrus user"
);
exit
(
1
);
}
get_prog
(
path
,
a
->
exec
);
syslog
(
LOG_DEBUG
,
"about to exec %s"
,
path
);
execv
(
path
,
a
->
exec
);
syslog
(
LOG_ERR
,
"can't exec %s on schedule: %m"
,
path
);
exit
(
1
);
break
;
default
:
/* we don't wait for it to complete */
/* add to child table */
c
=
get_centry
();
c
->
pid
=
p
;
c
->
s
=
NULL
;
c
->
next
=
ctable
[
p
%
child_table_size
];
ctable
[
p
%
child_table_size
]
=
c
;
break
;
}
b
=
a
->
next
;
if
(
a
->
period
)
{
a
->
mark
=
now
+
a
->
period
;
/* reschedule a */
schedule_event
(
a
);
}
else
{
free
(
a
);
}
/* examine next event */
a
=
b
;
}
}
void
reap_child
(
void
)
{
int
status
;
pid_t
pid
;
struct
centry
*
c
;
while
((
pid
=
waitpid
((
pid_t
)
-1
,
&
status
,
WNOHANG
))
>
0
)
{
if
(
WIFEXITED
(
status
))
{
syslog
(
LOG_DEBUG
,
"process %d exited, status %d"
,
pid
,
WEXITSTATUS
(
status
));
}
if
(
WIFSIGNALED
(
status
))
{
syslog
(
LOG_DEBUG
,
"process %d exited, signaled to death by %d"
,
pid
,
WTERMSIG
(
status
));
}
/* account for the child */
c
=
ctable
[
pid
%
child_table_size
];
if
(
c
&&
c
->
pid
==
pid
)
{
/* first thing in the linked list */
/* decrement active count for service */
if
(
c
->
s
)
c
->
s
->
nactive
--
;
ctable
[
pid
%
child_table_size
]
=
c
->
next
;
c
->
next
=
cfreelist
;
cfreelist
=
c
;
}
else
{
/* not the first thing in the linked list */
while
(
c
->
next
)
{
if
(
c
->
next
->
pid
==
pid
)
break
;
c
=
c
->
next
;
}
if
(
c
->
next
)
{
struct
centry
*
t
;
t
=
c
->
next
;
/* decrement active count for service */
if
(
t
->
s
)
t
->
s
->
nactive
--
;
c
->
next
=
t
->
next
;
/* remove node */
t
->
next
=
cfreelist
;
/* add to freelist */
cfreelist
=
t
;
}
else
{
/* yikes! don't know about this child! */
syslog
(
LOG_ERR
,
"process %d not recognized"
,
pid
);
}
}
}
}
static
int
gotsigchld
=
0
;
void
sigchld_handler
(
int
sig
)
{
gotsigchld
=
1
;
}
static
int
gotsighup
=
0
;
void
sighup_handler
(
int
sig
)
{
gotsighup
=
1
;
}
void
sigterm_handler
(
int
sig
)
{
struct
sigaction
action
;
/* send all the other processes SIGTERM, then exit */
sigemptyset
(
&
action
.
sa_mask
);
action
.
sa_flags
=
0
;
action
.
sa_handler
=
SIG_IGN
;
if
(
sigaction
(
SIGTERM
,
&
action
,
(
struct
sigaction
*
)
0
)
<
0
)
{
syslog
(
LOG_ERR
,
"sigaction: %m"
);
exit
(
1
);
}
/* kill my process group */
if
(
kill
(
0
,
SIGTERM
)
<
0
)
{
syslog
(
LOG_ERR
,
"kill(0, SIGTERM): %m"
);
}
#if HAVE_UCDSNMP
/* tell master agent we're exiting */
snmp_shutdown
(
"cyrusMaster"
);
#endif
syslog
(
LOG_INFO
,
"exiting on SIGTERM"
);
exit
(
0
);
}
void
sigalrm_handler
(
int
sig
)
{
}
void
sighandler_setup
(
void
)
{
struct
sigaction
action
;
sigemptyset
(
&
action
.
sa_mask
);
action
.
sa_flags
=
0
;
action
.
sa_handler
=
sighup_handler
;
#ifdef SA_RESTART
action
.
sa_flags
|=
SA_RESTART
;
#endif
if
(
sigaction
(
SIGHUP
,
&
action
,
NULL
)
<
0
)
{
fatal
(
"unable to install signal handler for SIGHUP: %m"
,
1
);
}
action
.
sa_handler
=
sigalrm_handler
;
if
(
sigaction
(
SIGALRM
,
&
action
,
NULL
)
<
0
)
{
fatal
(
"unable to install signal handler for SIGALRM: %m"
,
1
);
}
action
.
sa_handler
=
sigterm_handler
;
if
(
sigaction
(
SIGTERM
,
&
action
,
NULL
)
<
0
)
{
fatal
(
"unable to install signal handler for SIGTERM: %m"
,
1
);
}
action
.
sa_flags
|=
SA_NOCLDSTOP
;
action
.
sa_handler
=
sigchld_handler
;
if
(
sigaction
(
SIGCHLD
,
&
action
,
NULL
)
<
0
)
{
fatal
(
"unable to install signal handler for SIGCHLD: %m"
,
1
);
}
}
void
process_msg
(
struct
service
*
s
,
int
msg
)
{
switch
(
msg
)
{
case
MASTER_SERVICE_AVAILABLE
:
s
->
ready_workers
++
;
break
;
case
MASTER_SERVICE_UNAVAILABLE
:
s
->
ready_workers
--
;
break
;
default
:
syslog
(
LOG_ERR
,
"unrecognized message for service '%s': %x"
,
s
->
name
,
msg
);
break
;
}
if
(
verbose
)
syslog
(
LOG_DEBUG
,
"service %s now has %d workers
\n
"
,
s
->
name
,
s
->
ready_workers
);
}
static
char
**
tokenize
(
char
*
p
)
{
char
**
tokens
=
NULL
;
/* allocated in increments of 10 */
int
i
=
0
;
if
(
!
p
||
!*
p
)
return
NULL
;
/* sanity check */
while
(
*
p
)
{
while
(
*
p
&&
isspace
((
int
)
*
p
))
p
++
;
/* skip whitespace */
if
(
!
(
i
%
10
))
tokens
=
realloc
(
tokens
,
(
i
+
10
)
*
sizeof
(
char
*
));
if
(
!
tokens
)
return
NULL
;
/* got a token */
tokens
[
i
++
]
=
p
;
while
(
*
p
&&
!
isspace
((
int
)
*
p
))
p
++
;
/* p is whitespace or end of cmd */
if
(
*
p
)
*
p
++
=
'\0'
;
}
/* add a NULL on the end */
if
(
!
(
i
%
10
))
tokens
=
realloc
(
tokens
,
(
i
+
1
)
*
sizeof
(
char
*
));
if
(
!
tokens
)
return
NULL
;
tokens
[
i
]
=
NULL
;
return
tokens
;
}
void
add_start
(
const
char
*
name
,
struct
entry
*
e
,
void
*
rock
)
{
char
*
cmd
=
strdup
(
masterconf_getstring
(
e
,
"cmd"
,
NULL
));
char
buf
[
256
];
char
**
tok
;
if
(
!
cmd
)
{
snprintf
(
buf
,
sizeof
(
buf
),
"unable to find command for %s"
,
name
);
fatal
(
buf
,
EX_CONFIG
);
}
tok
=
tokenize
(
cmd
);
if
(
!
tok
)
fatal
(
"out of memory"
,
EX_UNAVAILABLE
);
run_startup
(
tok
);
free
(
tok
);
free
(
cmd
);
}
void
add_service
(
const
char
*
name
,
struct
entry
*
e
,
void
*
rock
)
{
char
*
cmd
=
strdup
(
masterconf_getstring
(
e
,
"cmd"
,
NULL
));
int
prefork
=
masterconf_getint
(
e
,
"prefork"
,
0
);
char
*
listen
=
strdup
(
masterconf_getstring
(
e
,
"listen"
,
NULL
));
char
*
proto
=
strdup
(
masterconf_getstring
(
e
,
"proto"
,
"tcp"
));
if
(
!
cmd
||
!
listen
)
{
char
buf
[
256
];
snprintf
(
buf
,
sizeof
(
buf
),
"unable to find command or port for %s"
,
name
);
fatal
(
buf
,
EX_CONFIG
);
}
if
(
nservices
==
allocservices
)
{
Services
=
realloc
(
Services
,
(
allocservices
+=
5
)
*
sizeof
(
struct
service
));
if
(
!
Services
)
fatal
(
"out of memory"
,
EX_UNAVAILABLE
);
}
Services
[
nservices
].
name
=
strdup
(
name
);
Services
[
nservices
].
listen
=
listen
;
Services
[
nservices
].
proto
=
proto
;
Services
[
nservices
].
exec
=
tokenize
(
cmd
);
if
(
!
Services
[
nservices
].
exec
)
fatal
(
"out of memory"
,
EX_UNAVAILABLE
);
Services
[
nservices
].
socket
=
0
;
Services
[
nservices
].
saddr
=
NULL
;
Services
[
nservices
].
ready_workers
=
0
;
Services
[
nservices
].
desired_workers
=
prefork
;
memset
(
Services
[
nservices
].
stat
,
0
,
sizeof
(
Services
[
nservices
].
stat
));
Services
[
nservices
].
nforks
=
0
;
Services
[
nservices
].
nactive
=
0
;
nservices
++
;
}
void
add_event
(
const
char
*
name
,
struct
entry
*
e
,
void
*
rock
)
{
char
*
cmd
=
strdup
(
masterconf_getstring
(
e
,
"cmd"
,
NULL
));
int
period
=
60
*
masterconf_getint
(
e
,
"period"
,
0
);
struct
event
*
evt
;
if
(
!
cmd
)
{
char
buf
[
256
];
snprintf
(
buf
,
sizeof
(
buf
),
"unable to find command or port for %s"
,
name
);
fatal
(
buf
,
EX_CONFIG
);
}
evt
=
(
struct
event
*
)
malloc
(
sizeof
(
struct
event
));
if
(
!
evt
)
fatal
(
"out of memory"
,
EX_UNAVAILABLE
);
evt
->
name
=
strdup
(
name
);
evt
->
mark
=
0
;
evt
->
period
=
period
;
evt
->
exec
=
tokenize
(
cmd
);
if
(
!
evt
->
exec
)
fatal
(
"out of memory"
,
EX_UNAVAILABLE
);
evt
->
next
=
schedule
;
schedule
=
evt
;
}
#ifdef HAVE_SETRLIMIT
#ifdef RLIMIT_NOFILE
# define RLIMIT_NUMFDS RLIMIT_NOFILE
#else
# ifdef RLIMIT_OFILE
# define RLIMIT_NUMFDS RLIMIT_OFILE
# endif
#endif
void
unlimit_fds
(
void
)
{
struct
rlimit
r
;
r
.
rlim_max
=
RLIM_INFINITY
;
if
(
setrlimit
(
RLIMIT_NUMFDS
,
&
r
)
<
0
)
{
syslog
(
LOG_ERR
,
"unable to unlimit the number of file descriptors avialable"
);
}
}
#else
void
unlimit_fds
(
void
)
{
}
#endif
int
main
(
int
argc
,
char
**
argv
,
char
**
envp
)
{
int
i
,
opt
;
extern
int
optind
;
extern
char
*
optarg
;
int
fd
;
fd_set
rfds
;
char
*
p
=
NULL
;
p
=
getenv
(
"CYRUS_VERBOSE"
);
if
(
p
)
verbose
=
atoi
(
p
)
+
1
;
while
((
opt
=
getopt
(
argc
,
argv
,
"l:"
))
!=
EOF
)
{
switch
(
opt
)
{
case
'l'
:
/* user defined listen queue backlog */
listen_queue_backlog
=
atoi
(
optarg
);
break
;
default
:
break
;
}
}
/* zero out the children table */
memset
(
&
ctable
,
0
,
sizeof
(
struct
centry
*
)
*
child_table_size
);
/* close stdin/out/err */
for
(
fd
=
0
;
fd
<
3
;
fd
++
)
{
close
(
fd
);
if
(
open
(
"/dev/null"
,
O_RDWR
,
0
)
!=
fd
)
fatal
(
"couldn't open /dev/null: %m"
,
2
);
}
/* we reserve fds 3 and 4 for children to communicate with us, so they
better be available. */
for
(
fd
=
3
;
fd
<
5
;
fd
++
)
{
close
(
fd
);
if
(
dup
(
0
)
!=
fd
)
fatal
(
"couldn't dup fd 0: %m"
,
2
);
}
masterconf_init
(
"master"
);
syslog
(
LOG_NOTICE
,
"process started"
);
#ifdef HAVE_UCDSNMP
/* initialize SNMP agent */
/* make us a agentx client. */
ds_set_boolean
(
DS_APPLICATION_ID
,
DS_AGENT_ROLE
,
1
);
/* initialize the agent library */
init_agent
(
"cyrusMaster"
);
init_cyrusMasterMIB
();
init_snmp
(
"cyrusMaster"
);
#endif
masterconf_getsection
(
"START"
,
&
add_start
,
NULL
);
masterconf_getsection
(
"SERVICES"
,
&
add_service
,
NULL
);
masterconf_getsection
(
"EVENTS"
,
&
add_event
,
NULL
);
/* set signal handlers */
sighandler_setup
();
/* initialize services */
for
(
i
=
0
;
i
<
nservices
;
i
++
)
{
service_create
(
&
Services
[
i
]);
if
(
verbose
>
2
)
syslog
(
LOG_DEBUG
,
"init: service %s socket %d pipe %d %d"
,
Services
[
i
].
name
,
Services
[
i
].
socket
,
Services
[
i
].
stat
[
0
],
Services
[
i
].
stat
[
1
]);
}
if
(
become_cyrus_early
)
become_cyrus
();
/* ok, we're going to start spawning like mad now */
syslog
(
LOG_NOTICE
,
"ready for work"
);
for
(;;)
{
int
r
,
i
,
msg
,
maxfd
;
struct
timeval
tv
,
*
tvptr
;
time_t
now
=
time
(
NULL
);
#if HAVE_UCDSNMP
int
blockp
=
0
;
#endif
/* run any scheduled processes */
spawn_schedule
(
now
);
tvptr
=
NULL
;
if
(
schedule
)
{
if
(
now
<
schedule
->
mark
)
tv
.
tv_sec
=
schedule
->
mark
-
now
;
else
tv
.
tv_sec
=
0
;
tv
.
tv_usec
=
0
;
tvptr
=
&
tv
;
}
/* do we have any services undermanned? */
for
(
i
=
0
;
i
<
nservices
;
i
++
)
{
if
(
Services
[
i
].
exec
/* enabled */
&&
(
Services
[
i
].
ready_workers
<
Services
[
i
].
desired_workers
))
{
spawn_service
(
&
Services
[
i
]);
}
}
if
(
gotsigchld
)
{
/* order matters here */
gotsigchld
=
0
;
reap_child
();
}
if
(
gotsighup
)
{
syslog
(
LOG_NOTICE
,
"got SIGHUP"
);
gotsighup
=
0
;
}
FD_ZERO
(
&
rfds
);
maxfd
=
0
;
for
(
i
=
0
;
i
<
nservices
;
i
++
)
{
int
x
=
Services
[
i
].
stat
[
0
];
int
y
=
Services
[
i
].
socket
;
/* messages */
if
(
x
>
0
)
{
if
(
verbose
>
2
)
syslog
(
LOG_DEBUG
,
"listening for messages from %s"
,
Services
[
i
].
name
);
FD_SET
(
x
,
&
rfds
);
}
if
(
x
>
maxfd
)
maxfd
=
x
;
/* connections */
if
(
y
>
0
&&
Services
[
i
].
ready_workers
==
0
)
{
if
(
verbose
>
2
)
syslog
(
LOG_DEBUG
,
"listening for connections for %s"
,
Services
[
i
].
name
);
FD_SET
(
y
,
&
rfds
);
if
(
y
>
maxfd
)
maxfd
=
y
;
}
/* paranoia */
if
(
Services
[
i
].
ready_workers
<
0
)
{
syslog
(
LOG_ERR
,
"%s has %d workers?!?"
,
Services
[
i
].
name
,
Services
[
i
].
ready_workers
);
}
}
maxfd
++
;
/* need 1 greater than maxfd */
#ifdef HAVE_UCDSNMP
if
(
tvptr
==
NULL
)
blockp
=
1
;
snmp_select_info
(
&
maxfd
,
&
rfds
,
tvptr
,
&
blockp
);
#endif
errno
=
0
;
r
=
select
(
maxfd
,
&
rfds
,
NULL
,
NULL
,
tvptr
);
if
(
r
==
-1
&&
errno
==
EAGAIN
)
continue
;
if
(
r
==
-1
&&
errno
==
EINTR
)
continue
;
if
(
r
==
-1
)
{
/* uh oh */
fatal
(
"select failed: %m"
,
1
);
}
#ifdef HAVE_UCDSNMP
/* check for SNMP queries */
snmp_read
(
&
rfds
);
snmp_timeout
();
#endif
for
(
i
=
0
;
i
<
nservices
;
i
++
)
{
int
x
=
Services
[
i
].
stat
[
0
];
int
y
=
Services
[
i
].
socket
;
if
(
FD_ISSET
(
x
,
&
rfds
))
{
r
=
read
(
x
,
&
msg
,
sizeof
(
int
));
if
(
r
!=
sizeof
(
int
))
{
syslog
(
LOG_ERR
,
"got weird response from child: %x"
,
i
);
continue
;
}
process_msg
(
&
Services
[
i
],
msg
);
}
if
(
Services
[
i
].
ready_workers
==
0
&&
FD_ISSET
(
y
,
&
rfds
))
{
/* huh, someone wants to talk to us */
spawn_service
(
&
Services
[
i
]);
}
}
}
}
File Metadata
Details
Attached
Mime Type
text/x-c
Expires
Sat, Apr 4, 1:45 AM (1 w, 5 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
18822003
Default Alt Text
master.c (24 KB)
Attached To
Mode
R111 cyrus-imapd
Attached
Detach File
Event Timeline