commit
70c78281e3
20 changed files with 4682 additions and 0 deletions
-
30Makefile.in
-
90cfg_file.h
-
174cfg_file.l
-
194cfg_file.y
-
144cfg_utils.c
-
331compat/md5.c
-
53compat/md5.h
-
618compat/queue.h
-
70compat/strlcpy.c
-
8compat/strlcpy.h
-
516configure
-
324main.c
-
71main.h
-
743memcached.c
-
113memcached.h
-
521upstream.c
-
43upstream.h
-
534util.c
-
53util.h
-
52worker.c
@ -0,0 +1,30 @@ |
|||
|
|||
all: $(TARGETS) |
|||
|
|||
memctest: upstream.c memcached.c memcached-test.c |
|||
$(CC) $(OPT_FLAGS) $(CFLAGS) $(PTHREAD_CFLAGS) -c upstream.c |
|||
$(CC) $(OPT_FLAGS) $(CFLAGS) $(PTHREAD_CFLAGS) -c memcached.c |
|||
$(CC) $(OPT_FLAGS) $(CFLAGS) $(PTHREAD_CFLAGS) -c memcached-test.c |
|||
$(CC) $(OPT_FLAGS) $(PTHREAD_LDFLAGS) $(LD_PATH) upstream.o memcached.o memcached-test.o $(LIBS) -o memcached-test |
|||
|
|||
install: $(EXEC) rmilter.8 rmilter.conf.sample |
|||
$(INSTALL) -b $(EXEC) $(PREFIX)/sbin/$(EXEC) |
|||
$(INSTALL) -v $(EXEC).sh $(PREFIX)/etc/rc.d |
|||
#$(INSTALL) -m0644 rspamd.8 $(MANPATH)/man8 |
|||
#$(INSTALL) -m0644 rspamd.conf.sample $(PREFIX)/etc |
|||
$(MKDIR) -o $(RSPAMD_USER) -g $(RSPAMD_GROUP) /var/run/rspamd |
|||
|
|||
clean: |
|||
rm -f *.o $(EXEC) *.core |
|||
rm -f cfg_lex.c cfg_yacc.c cfg_yacc.h |
|||
|
|||
dist-clean: clean |
|||
rm -f Makefile |
|||
rm -f config.log |
|||
rm -f md5.h md5.c strlcpy.h strlcpy.c queue.h |
|||
|
|||
creategroup: |
|||
@echo "Create group $(RSPAMD_GROUP) before installing!" |
|||
|
|||
createuser: |
|||
@echo "Create user $(RSPAMD_USER) before installing!" |
|||
@ -0,0 +1,90 @@ |
|||
/* |
|||
* $Id$ |
|||
*/ |
|||
|
|||
|
|||
#ifndef CFG_FILE_H |
|||
#define CFG_FILE_H |
|||
|
|||
#include <sys/types.h> |
|||
#ifndef OWN_QUEUE_H |
|||
#include <sys/queue.h> |
|||
#else |
|||
#include "queue.h" |
|||
#endif |
|||
#include <netinet/in.h> |
|||
#include <sys/un.h> |
|||
#include "upstream.h" |
|||
#include "memcached.h" |
|||
|
|||
#define DEFAULT_BIND_PORT 768 |
|||
#define MAX_MEMCACHED_SERVERS 48 |
|||
#define DEFAULT_MEMCACHED_PORT 11211 |
|||
/* Memcached timeouts */ |
|||
#define DEFAULT_MEMCACHED_CONNECT_TIMEOUT 1000 |
|||
/* Upstream timeouts */ |
|||
#define DEFAULT_UPSTREAM_ERROR_TIME 10 |
|||
#define DEFAULT_UPSTREAM_ERROR_TIME 10 |
|||
#define DEFAULT_UPSTREAM_DEAD_TIME 300 |
|||
#define DEFAULT_UPSTREAM_MAXERRORS 10 |
|||
|
|||
/* 1 worker by default */ |
|||
#define DEFAULT_WORKERS_NUM 1 |
|||
|
|||
#define yyerror(fmt, ...) \ |
|||
fprintf (stderr, "Config file parse error!\non line: %d\n", yylineno); \ |
|||
fprintf (stderr, "while reading text: %s\nreason: ", yytext); \ |
|||
fprintf (stderr, fmt, ##__VA_ARGS__); \ |
|||
fprintf (stderr, "\n") |
|||
#define yywarn(fmt, ...) \ |
|||
fprintf (stderr, "Config file parse warning!\non line %d\n", yylineno); \ |
|||
fprintf (stderr, "while reading text: %s\nreason: ", yytext); \ |
|||
fprintf (stderr, fmt, ##__VA_ARGS__); \ |
|||
fprintf (stderr, "\n") |
|||
|
|||
|
|||
enum { VAL_UNDEF=0, VAL_TRUE, VAL_FALSE }; |
|||
|
|||
struct memcached_server { |
|||
struct upstream up; |
|||
struct in_addr addr; |
|||
uint16_t port; |
|||
short alive; |
|||
short int num; |
|||
}; |
|||
|
|||
struct config_file { |
|||
char *cfg_name; |
|||
char *pid_file; |
|||
char *temp_dir; |
|||
|
|||
char *bind_host; |
|||
struct in_addr bind_addr; |
|||
uint16_t bind_port; |
|||
uint16_t bind_family; |
|||
|
|||
char no_fork; |
|||
unsigned int workers_number; |
|||
|
|||
struct memcached_server memcached_servers[MAX_MEMCACHED_SERVERS]; |
|||
size_t memcached_servers_num; |
|||
memc_proto_t memcached_protocol; |
|||
unsigned int memcached_error_time; |
|||
unsigned int memcached_dead_time; |
|||
unsigned int memcached_maxerrors; |
|||
unsigned int memcached_connect_timeout; |
|||
}; |
|||
|
|||
int add_memcached_server (struct config_file *cf, char *str); |
|||
int parse_bind_line (struct config_file *cf, char *str); |
|||
void init_defaults (struct config_file *cfg); |
|||
void free_config (struct config_file *cfg); |
|||
|
|||
int yylex (void); |
|||
int yyparse (void); |
|||
void yyrestart (FILE *); |
|||
|
|||
#endif /* ifdef CFG_FILE_H */ |
|||
/* |
|||
* vi:ts=4 |
|||
*/ |
|||
@ -0,0 +1,174 @@ |
|||
%x incl |
|||
|
|||
%{ |
|||
#include <stdio.h> |
|||
#include <stdlib.h> |
|||
#include <string.h> |
|||
#include <strings.h> |
|||
#include <syslog.h> |
|||
#include "cfg_file.h" |
|||
#include "cfg_yacc.h" |
|||
|
|||
#define MAX_INCLUDE_DEPTH 10 |
|||
YY_BUFFER_STATE include_stack[MAX_INCLUDE_DEPTH]; |
|||
int include_stack_ptr = 0; |
|||
|
|||
static size_t |
|||
parse_limit (const char *limit) |
|||
{ |
|||
size_t result = 0; |
|||
char *err_str; |
|||
|
|||
if (!limit || *limit == '\0') return 0; |
|||
|
|||
result = strtoul (limit, &err_str, 10); |
|||
|
|||
if (*err_str != '\0') { |
|||
/* Megabytes */ |
|||
if (*err_str == 'm' || *err_str == 'M') { |
|||
result *= 1048576L; |
|||
} |
|||
/* Kilobytes */ |
|||
else if (*err_str == 'k' || *err_str == 'K') { |
|||
result *= 1024; |
|||
} |
|||
/* Gigabytes */ |
|||
else if (*err_str == 'g' || *err_str == 'G') { |
|||
result *= 1073741824L; |
|||
} |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
static unsigned int |
|||
parse_seconds (const char *t) |
|||
{ |
|||
unsigned int result = 0; |
|||
char *err_str; |
|||
|
|||
if (!t || *t == '\0') return 0; |
|||
|
|||
result = strtoul (t, &err_str, 10); |
|||
|
|||
if (*err_str != '\0') { |
|||
/* Seconds */ |
|||
if (*err_str == 's' || *err_str == 'S') { |
|||
result *= 1000; |
|||
} |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
static char |
|||
parse_flag (const char *str) |
|||
{ |
|||
if (!str || !*str) return -1; |
|||
|
|||
if ((*str == 'Y' || *str == 'y') && *(str + 1) == '\0') { |
|||
return 1; |
|||
} |
|||
|
|||
if ((*str == 'Y' || *str == 'y') && |
|||
(*(str + 1) == 'E' || *(str + 1) == 'e') && |
|||
(*(str + 2) == 'S' || *(str + 2) == 's') && |
|||
*(str + 3) == '\0') { |
|||
return 1; |
|||
} |
|||
|
|||
if ((*str == 'N' || *str == 'n') && *(str + 1) == '\0') { |
|||
return 0; |
|||
} |
|||
|
|||
if ((*str == 'N' || *str == 'n') && |
|||
(*(str + 1) == 'O' || *(str + 1) == 'o') && |
|||
*(str + 2) == '\0') { |
|||
return 0; |
|||
} |
|||
|
|||
return -1; |
|||
} |
|||
|
|||
%} |
|||
|
|||
%option noyywrap |
|||
%option yylineno |
|||
|
|||
%% |
|||
^[ \t]*#.* /* ignore comments */; |
|||
.include BEGIN(incl); |
|||
tempdir return TEMPDIR; |
|||
pidfile return PIDFILE; |
|||
workers return WORKERS; |
|||
error_time return ERROR_TIME; |
|||
dead_time return DEAD_TIME; |
|||
maxerrors return MAXERRORS; |
|||
reconnect_timeout return RECONNECT_TIMEOUT; |
|||
connect_timeout return CONNECT_TIMEOUT; |
|||
protocol return PROTOCOL; |
|||
memcached return MEMCACHED; |
|||
bind_socket return BINDSOCK; |
|||
servers return SERVERS; |
|||
|
|||
\{ return OBRACE; |
|||
\} return EBRACE; |
|||
; return SEMICOLON; |
|||
, return COMMA; |
|||
= return EQSIGN; |
|||
yes|YES|no|NO|[yY]|[nN] yylval.flag=parse_flag(yytext); return FLAG; |
|||
\n /* ignore EOL */; |
|||
[ \t]+ /* ignore whitespace */; |
|||
\"[^"]+\" yylval.string=strdup(yytext + 1); yylval.string[strlen(yylval.string) - 1] = '\0'; return QUOTEDSTRING; |
|||
\" return QUOTE; |
|||
[0-9]+ yylval.number=strtol(yytext, NULL, 10); return NUMBER; |
|||
[0-9]+[kKmMgG]? yylval.limit=parse_limit(yytext); return SIZELIMIT; |
|||
[0-9]+[sS]|[0-9]+[mM][sS] yylval.seconds=parse_seconds(yytext); return SECONDS; |
|||
[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3} yylval.string=strdup(yytext); return IPADDR; |
|||
[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\/[0-9]{1,2} yylval.string=strdup(yytext); return IPNETWORK; |
|||
[a-zA-Z<][a-zA-Z@+>_-]* yylval.string=strdup(yytext); return STRING; |
|||
\/[^/\n]+\/ yylval.string=strdup(yytext); return REGEXP; |
|||
[a-zA-Z0-9].[a-zA-Z0-9\/.-]+ yylval.string=strdup(yytext); return DOMAIN; |
|||
[a-zA-Z0-9.-]+:[0-9]{1,5} yylval.string=strdup(yytext); return HOSTPORT; |
|||
[a-zA-Z0-9\/.-]+ yylval.string=strdup(yytext); return FILENAME; |
|||
<incl>[ \t]* /* eat the whitespace */ |
|||
<incl>[^ \t\n]+ { /* got the include file name */ |
|||
if ( include_stack_ptr >= MAX_INCLUDE_DEPTH ) { |
|||
yyerror ("yylex: includes nested too deeply" ); |
|||
return -1; |
|||
} |
|||
|
|||
include_stack[include_stack_ptr++] = |
|||
YY_CURRENT_BUFFER; |
|||
|
|||
yyin = fopen( yytext, "r" ); |
|||
|
|||
if ( ! yyin ) { |
|||
yyerror("yylex: cannot open include file"); |
|||
return -1; |
|||
} |
|||
|
|||
yy_switch_to_buffer( |
|||
yy_create_buffer( yyin, YY_BUF_SIZE ) ); |
|||
|
|||
BEGIN(INITIAL); |
|||
} |
|||
|
|||
<<EOF>> { |
|||
if ( --include_stack_ptr < 0 ) |
|||
{ |
|||
yyterminate(); |
|||
} |
|||
|
|||
else |
|||
{ |
|||
yy_delete_buffer( YY_CURRENT_BUFFER ); |
|||
yy_switch_to_buffer( |
|||
include_stack[include_stack_ptr] ); |
|||
} |
|||
} |
|||
|
|||
%% |
|||
/* |
|||
* vi:ts=4 |
|||
*/ |
|||
@ -0,0 +1,194 @@ |
|||
/* $Id$ */ |
|||
|
|||
%{ |
|||
|
|||
#include <ctype.h> |
|||
#include <errno.h> |
|||
#include <stdarg.h> |
|||
#include <stdio.h> |
|||
#include <stdlib.h> |
|||
#include <string.h> |
|||
#include <sys/queue.h> |
|||
#include <syslog.h> |
|||
#include <netinet/in.h> |
|||
#include <arpa/inet.h> |
|||
|
|||
#include "cfg_file.h" |
|||
|
|||
#define YYDEBUG 0 |
|||
|
|||
extern struct config_file *cfg; |
|||
extern int yylineno; |
|||
extern char *yytext; |
|||
|
|||
|
|||
%} |
|||
%union |
|||
{ |
|||
char *string; |
|||
size_t limit; |
|||
char flag; |
|||
unsigned int seconds; |
|||
unsigned int number; |
|||
} |
|||
|
|||
%token ERROR STRING QUOTEDSTRING FLAG |
|||
%token FILENAME REGEXP QUOTE SEMICOLON OBRACE EBRACE COMMA EQSIGN |
|||
%token BINDSOCK SOCKCRED DOMAIN IPADDR IPNETWORK HOSTPORT NUMBER CHECK_TIMEOUT |
|||
%token MAXSIZE SIZELIMIT SECONDS BEANSTALK MYSQL USER PASSWORD DATABASE |
|||
%token TEMPDIR PIDFILE SERVERS ERROR_TIME DEAD_TIME MAXERRORS CONNECT_TIMEOUT PROTOCOL RECONNECT_TIMEOUT |
|||
%token READ_SERVERS WRITE_SERVER DIRECTORY_SERVERS MAILBOX_QUERY USERS_QUERY LASTLOGIN_QUERY |
|||
%token MEMCACHED WORKERS |
|||
|
|||
%type <string> STRING |
|||
%type <string> QUOTEDSTRING |
|||
%type <string> FILENAME |
|||
%type <string> SOCKCRED |
|||
%type <string> IPADDR IPNETWORK |
|||
%type <string> HOSTPORT |
|||
%type <string> DOMAIN |
|||
%type <limit> SIZELIMIT |
|||
%type <flag> FLAG |
|||
%type <seconds> SECONDS |
|||
%type <number> NUMBER |
|||
%type <string> memcached_hosts bind_cred |
|||
%% |
|||
|
|||
file : /* empty */ |
|||
| file command SEMICOLON { } |
|||
; |
|||
|
|||
command : |
|||
bindsock |
|||
| tempdir |
|||
| pidfile |
|||
| memcached |
|||
| workers |
|||
; |
|||
|
|||
tempdir : |
|||
TEMPDIR EQSIGN FILENAME { |
|||
cfg->temp_dir = $3; |
|||
} |
|||
; |
|||
|
|||
pidfile : |
|||
PIDFILE EQSIGN FILENAME { |
|||
cfg->pid_file = $3; |
|||
} |
|||
; |
|||
|
|||
bindsock: |
|||
BINDSOCK EQSIGN bind_cred { |
|||
if (!parse_bind_line (cfg, $3)) { |
|||
yyerror ("yyparse: parse_bind_line"); |
|||
YYERROR; |
|||
} |
|||
free ($3); |
|||
} |
|||
; |
|||
|
|||
bind_cred: |
|||
STRING { |
|||
$$ = $1; |
|||
} |
|||
| IPADDR{ |
|||
$$ = $1; |
|||
} |
|||
| DOMAIN { |
|||
$$ = $1; |
|||
} |
|||
| HOSTPORT { |
|||
$$ = $1; |
|||
} |
|||
| FILENAME { |
|||
$$ = $1; |
|||
} |
|||
; |
|||
|
|||
memcached: |
|||
BEANSTALK OBRACE memcachedbody EBRACE |
|||
; |
|||
|
|||
memcachedbody: |
|||
memcachedcmd SEMICOLON |
|||
| memcachedbody memcachedcmd SEMICOLON |
|||
; |
|||
|
|||
memcachedcmd: |
|||
memcached_servers |
|||
| memcached_connect_timeout |
|||
| memcached_error_time |
|||
| memcached_dead_time |
|||
| memcached_maxerrors |
|||
| memcached_protocol |
|||
; |
|||
|
|||
memcached_servers: |
|||
SERVERS EQSIGN memcached_server |
|||
; |
|||
|
|||
memcached_server: |
|||
memcached_params |
|||
| memcached_server COMMA memcached_params |
|||
; |
|||
|
|||
memcached_params: |
|||
memcached_hosts { |
|||
if (!add_memcached_server (cfg, $1)) { |
|||
yyerror ("yyparse: add_memcached_server"); |
|||
YYERROR; |
|||
} |
|||
free ($1); |
|||
} |
|||
; |
|||
memcached_hosts: |
|||
STRING |
|||
| IPADDR |
|||
| DOMAIN |
|||
| HOSTPORT |
|||
; |
|||
memcached_error_time: |
|||
ERROR_TIME EQSIGN NUMBER { |
|||
cfg->memcached_error_time = $3; |
|||
} |
|||
; |
|||
memcached_dead_time: |
|||
DEAD_TIME EQSIGN NUMBER { |
|||
cfg->memcached_dead_time = $3; |
|||
} |
|||
; |
|||
memcached_maxerrors: |
|||
MAXERRORS EQSIGN NUMBER { |
|||
cfg->memcached_maxerrors = $3; |
|||
} |
|||
; |
|||
memcached_connect_timeout: |
|||
CONNECT_TIMEOUT EQSIGN SECONDS { |
|||
cfg->memcached_connect_timeout = $3; |
|||
} |
|||
; |
|||
|
|||
memcached_protocol: |
|||
PROTOCOL EQSIGN STRING { |
|||
if (strncasecmp ($3, "udp", sizeof ("udp") - 1) == 0) { |
|||
cfg->memcached_protocol = UDP_TEXT; |
|||
} |
|||
else if (strncasecmp ($3, "tcp", sizeof ("tcp") - 1) == 0) { |
|||
cfg->memcached_protocol = TCP_TEXT; |
|||
} |
|||
else { |
|||
yyerror ("yyparse: cannot recognize protocol: %s", $3); |
|||
YYERROR; |
|||
} |
|||
} |
|||
; |
|||
workers: |
|||
WORKERS EQSIGN NUMBER { |
|||
cfg->workers_number = $3; |
|||
} |
|||
; |
|||
%% |
|||
/* |
|||
* vi:ts=4 |
|||
*/ |
|||
@ -0,0 +1,144 @@ |
|||
#include <ctype.h> |
|||
#include <errno.h> |
|||
#include <stdarg.h> |
|||
#include <stdio.h> |
|||
#include <stdlib.h> |
|||
#include <string.h> |
|||
#include <libmilter/mfapi.h> |
|||
#include <sys/queue.h> |
|||
#include <sys/un.h> |
|||
#include <netinet/in.h> |
|||
#include <arpa/inet.h> |
|||
#include <syslog.h> |
|||
#include <netdb.h> |
|||
#include <math.h> |
|||
|
|||
#include "cfg_file.h" |
|||
#include "memcached.h" |
|||
|
|||
extern int yylineno; |
|||
extern char *yytext; |
|||
|
|||
|
|||
int |
|||
add_memcached_server (struct config_file *cf, char *str) |
|||
{ |
|||
char *cur_tok, *err_str; |
|||
struct memcached_server *mc; |
|||
struct hostent *he; |
|||
uint16_t port; |
|||
|
|||
if (str == NULL) return 0; |
|||
|
|||
cur_tok = strsep (&str, ":"); |
|||
|
|||
if (cur_tok == NULL || *cur_tok == '\0') return 0; |
|||
|
|||
if(cf->memcached_servers_num == MAX_MEMCACHED_SERVERS) { |
|||
yywarn ("yyparse: maximum number of memcached servers is reached %d", MAX_MEMCACHED_SERVERS); |
|||
} |
|||
|
|||
mc = &cf->memcached_servers[cf->memcached_servers_num]; |
|||
if (mc == NULL) return 0; |
|||
/* cur_tok - server name, str - server port */ |
|||
if (str == NULL) { |
|||
port = DEFAULT_MEMCACHED_PORT; |
|||
} |
|||
else { |
|||
port = (uint16_t)strtoul (str, &err_str, 10); |
|||
if (*err_str != '\0') { |
|||
return 0; |
|||
} |
|||
} |
|||
|
|||
if (!inet_aton (cur_tok, &mc->addr)) { |
|||
/* Try to call gethostbyname */ |
|||
he = gethostbyname (cur_tok); |
|||
if (he == NULL) { |
|||
return 0; |
|||
} |
|||
else { |
|||
memcpy((char *)&mc->addr, he->h_addr, sizeof(struct in_addr)); |
|||
} |
|||
} |
|||
mc->port = port; |
|||
cf->memcached_servers_num++; |
|||
return 1; |
|||
} |
|||
|
|||
int |
|||
parse_bind_line (struct config_file *cf, char *str) |
|||
{ |
|||
char *cur_tok, *err_str; |
|||
struct hostent *he; |
|||
size_t s; |
|||
|
|||
if (str == NULL) return 0; |
|||
|
|||
cur_tok = strsep (&str, ":"); |
|||
|
|||
if (cur_tok[0] == '/' || cur_tok[0] == '.') { |
|||
cf->bind_host = strdup (cur_tok); |
|||
cf->bind_family = AF_UNIX; |
|||
return 1; |
|||
|
|||
} else { |
|||
if (str == '\0') { |
|||
cf->bind_port = DEFAULT_BIND_PORT; |
|||
} |
|||
else { |
|||
cf->bind_port = (uint16_t)strtoul (str, &err_str, 10); |
|||
if (*err_str != '\0') { |
|||
return 0; |
|||
} |
|||
} |
|||
|
|||
if (!inet_aton (cur_tok, &cf->bind_addr)) { |
|||
/* Try to call gethostbyname */ |
|||
he = gethostbyname (cur_tok); |
|||
if (he == NULL) { |
|||
return 0; |
|||
} |
|||
else { |
|||
cf->bind_host = strdup (cur_tok); |
|||
memcpy((char *)&cf->bind_addr, he->h_addr, sizeof(struct in_addr)); |
|||
s = strlen (cur_tok) + 1; |
|||
} |
|||
} |
|||
|
|||
cf->bind_family = AF_INET; |
|||
|
|||
return 1; |
|||
} |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
void |
|||
init_defaults (struct config_file *cfg) |
|||
{ |
|||
cfg->memcached_error_time = DEFAULT_UPSTREAM_ERROR_TIME; |
|||
cfg->memcached_dead_time = DEFAULT_UPSTREAM_DEAD_TIME; |
|||
cfg->memcached_maxerrors = DEFAULT_UPSTREAM_MAXERRORS; |
|||
cfg->memcached_protocol = TCP_TEXT; |
|||
|
|||
cfg->workers_number = DEFAULT_WORKERS_NUM; |
|||
} |
|||
|
|||
void |
|||
free_config (struct config_file *cfg) |
|||
{ |
|||
if (cfg->pid_file) { |
|||
free (cfg->pid_file); |
|||
} |
|||
if (cfg->temp_dir) { |
|||
free (cfg->temp_dir); |
|||
} |
|||
if (cfg->bind_host) { |
|||
free (cfg->bind_host); |
|||
} |
|||
} |
|||
|
|||
/* |
|||
* vi:ts=4 |
|||
*/ |
|||
@ -0,0 +1,331 @@ |
|||
/* |
|||
* MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm |
|||
* |
|||
* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All |
|||
* rights reserved. |
|||
* |
|||
* License to copy and use this software is granted provided that it |
|||
* is identified as the "RSA Data Security, Inc. MD5 Message-Digest |
|||
* Algorithm" in all material mentioning or referencing this software |
|||
* or this function. |
|||
* |
|||
* License is also granted to make and use derivative works provided |
|||
* that such works are identified as "derived from the RSA Data |
|||
* Security, Inc. MD5 Message-Digest Algorithm" in all material |
|||
* mentioning or referencing the derived work. |
|||
* |
|||
* RSA Data Security, Inc. makes no representations concerning either |
|||
* the merchantability of this software or the suitability of this |
|||
* software for any particular purpose. It is provided "as is" |
|||
* without express or implied warranty of any kind. |
|||
* |
|||
* These notices must be retained in any copies of any part of this |
|||
* documentation and/or software. |
|||
* |
|||
* This code is the same as the code published by RSA Inc. It has been |
|||
* edited for clarity and style only. |
|||
*/ |
|||
|
|||
#include <sys/cdefs.h> |
|||
|
|||
#include <sys/types.h> |
|||
|
|||
#include <string.h> |
|||
|
|||
#ifdef HAVE_ENDIAN_H |
|||
#include <endian.h> |
|||
#endif |
|||
#ifdef HAVE_MACHINE_ENDIAN_H |
|||
#include <machine/endian.h> |
|||
#endif |
|||
#include "md5.h" |
|||
|
|||
static void MD5Transform(u_int32_t [4], const unsigned char [64]); |
|||
|
|||
#if (BYTE_ORDER == LITTLE_ENDIAN) |
|||
#define Encode memcpy |
|||
#define Decode memcpy |
|||
#else |
|||
|
|||
/* |
|||
* Encodes input (u_int32_t) into output (unsigned char). Assumes len is |
|||
* a multiple of 4. |
|||
*/ |
|||
|
|||
static void |
|||
Encode (unsigned char *output, u_int32_t *input, unsigned int len) |
|||
{ |
|||
unsigned int i; |
|||
u_int32_t *op = (u_int32_t *)output; |
|||
|
|||
for (i = 0; i < len / 4; i++) |
|||
op[i] = htole32(input[i]); |
|||
} |
|||
|
|||
/* |
|||
* Decodes input (unsigned char) into output (u_int32_t). Assumes len is |
|||
* a multiple of 4. |
|||
*/ |
|||
|
|||
static void |
|||
Decode (u_int32_t *output, const unsigned char *input, unsigned int len) |
|||
{ |
|||
unsigned int i; |
|||
const u_int32_t *ip = (const u_int32_t *)input; |
|||
|
|||
for (i = 0; i < len / 4; i++) |
|||
output[i] = le32toh(ip[i]); |
|||
} |
|||
#endif |
|||
|
|||
static unsigned char PADDING[64] = { |
|||
0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
|||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
|||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 |
|||
}; |
|||
|
|||
/* F, G, H and I are basic MD5 functions. */ |
|||
#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) |
|||
#define G(x, y, z) (((x) & (z)) | ((y) & (~z))) |
|||
#define H(x, y, z) ((x) ^ (y) ^ (z)) |
|||
#define I(x, y, z) ((y) ^ ((x) | (~z))) |
|||
|
|||
/* ROTATE_LEFT rotates x left n bits. */ |
|||
#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) |
|||
|
|||
/* |
|||
* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. |
|||
* Rotation is separate from addition to prevent recomputation. |
|||
*/ |
|||
#define FF(a, b, c, d, x, s, ac) { \ |
|||
(a) += F ((b), (c), (d)) + (x) + (u_int32_t)(ac); \ |
|||
(a) = ROTATE_LEFT ((a), (s)); \ |
|||
(a) += (b); \ |
|||
} |
|||
#define GG(a, b, c, d, x, s, ac) { \ |
|||
(a) += G ((b), (c), (d)) + (x) + (u_int32_t)(ac); \ |
|||
(a) = ROTATE_LEFT ((a), (s)); \ |
|||
(a) += (b); \ |
|||
} |
|||
#define HH(a, b, c, d, x, s, ac) { \ |
|||
(a) += H ((b), (c), (d)) + (x) + (u_int32_t)(ac); \ |
|||
(a) = ROTATE_LEFT ((a), (s)); \ |
|||
(a) += (b); \ |
|||
} |
|||
#define II(a, b, c, d, x, s, ac) { \ |
|||
(a) += I ((b), (c), (d)) + (x) + (u_int32_t)(ac); \ |
|||
(a) = ROTATE_LEFT ((a), (s)); \ |
|||
(a) += (b); \ |
|||
} |
|||
|
|||
/* MD5 initialization. Begins an MD5 operation, writing a new context. */ |
|||
|
|||
void |
|||
MD5Init (context) |
|||
MD5_CTX *context; |
|||
{ |
|||
|
|||
context->count[0] = context->count[1] = 0; |
|||
|
|||
/* Load magic initialization constants. */ |
|||
context->state[0] = 0x67452301; |
|||
context->state[1] = 0xefcdab89; |
|||
context->state[2] = 0x98badcfe; |
|||
context->state[3] = 0x10325476; |
|||
} |
|||
|
|||
/* |
|||
* MD5 block update operation. Continues an MD5 message-digest |
|||
* operation, processing another message block, and updating the |
|||
* context. |
|||
*/ |
|||
|
|||
void |
|||
MD5Update (context, in, inputLen) |
|||
MD5_CTX *context; |
|||
const void *in; |
|||
unsigned int inputLen; |
|||
{ |
|||
unsigned int i, idx, partLen; |
|||
const unsigned char *input = in; |
|||
|
|||
/* Compute number of bytes mod 64 */ |
|||
idx = (unsigned int)((context->count[0] >> 3) & 0x3F); |
|||
|
|||
/* Update number of bits */ |
|||
if ((context->count[0] += ((u_int32_t)inputLen << 3)) |
|||
< ((u_int32_t)inputLen << 3)) |
|||
context->count[1]++; |
|||
context->count[1] += ((u_int32_t)inputLen >> 29); |
|||
|
|||
partLen = 64 - idx; |
|||
|
|||
/* Transform as many times as possible. */ |
|||
if (inputLen >= partLen) { |
|||
memcpy((void *)&context->buffer[idx], (const void *)input, |
|||
partLen); |
|||
MD5Transform (context->state, context->buffer); |
|||
|
|||
for (i = partLen; i + 63 < inputLen; i += 64) |
|||
MD5Transform (context->state, &input[i]); |
|||
|
|||
idx = 0; |
|||
} |
|||
else |
|||
i = 0; |
|||
|
|||
/* Buffer remaining input */ |
|||
memcpy ((void *)&context->buffer[idx], (const void *)&input[i], |
|||
inputLen-i); |
|||
} |
|||
|
|||
/* |
|||
* MD5 padding. Adds padding followed by original length. |
|||
*/ |
|||
|
|||
void |
|||
MD5Pad (context) |
|||
MD5_CTX *context; |
|||
{ |
|||
unsigned char bits[8]; |
|||
unsigned int idx, padLen; |
|||
|
|||
/* Save number of bits */ |
|||
Encode (bits, context->count, 8); |
|||
|
|||
/* Pad out to 56 mod 64. */ |
|||
idx = (unsigned int)((context->count[0] >> 3) & 0x3f); |
|||
padLen = (idx < 56) ? (56 - idx) : (120 - idx); |
|||
MD5Update (context, PADDING, padLen); |
|||
|
|||
/* Append length (before padding) */ |
|||
MD5Update (context, bits, 8); |
|||
} |
|||
|
|||
/* |
|||
* MD5 finalization. Ends an MD5 message-digest operation, writing the |
|||
* the message digest and zeroizing the context. |
|||
*/ |
|||
|
|||
void |
|||
MD5Final (digest, context) |
|||
unsigned char digest[16]; |
|||
MD5_CTX *context; |
|||
{ |
|||
/* Do padding. */ |
|||
MD5Pad (context); |
|||
|
|||
/* Store state in digest */ |
|||
Encode (digest, context->state, 16); |
|||
|
|||
/* Zeroize sensitive information. */ |
|||
memset ((void *)context, 0, sizeof (*context)); |
|||
} |
|||
|
|||
/* MD5 basic transformation. Transforms state based on block. */ |
|||
|
|||
static void |
|||
MD5Transform (state, block) |
|||
u_int32_t state[4]; |
|||
const unsigned char block[64]; |
|||
{ |
|||
u_int32_t a = state[0], b = state[1], c = state[2], d = state[3], x[16]; |
|||
|
|||
Decode (x, block, 64); |
|||
|
|||
/* Round 1 */ |
|||
#define S11 7 |
|||
#define S12 12 |
|||
#define S13 17 |
|||
#define S14 22 |
|||
FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */ |
|||
FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */ |
|||
FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */ |
|||
FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */ |
|||
FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */ |
|||
FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */ |
|||
FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */ |
|||
FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */ |
|||
FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */ |
|||
FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */ |
|||
FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ |
|||
FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ |
|||
FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ |
|||
FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ |
|||
FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ |
|||
FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ |
|||
|
|||
/* Round 2 */ |
|||
#define S21 5 |
|||
#define S22 9 |
|||
#define S23 14 |
|||
#define S24 20 |
|||
GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */ |
|||
GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */ |
|||
GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ |
|||
GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */ |
|||
GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */ |
|||
GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */ |
|||
GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ |
|||
GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */ |
|||
GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */ |
|||
GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ |
|||
GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */ |
|||
GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */ |
|||
GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ |
|||
GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */ |
|||
GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */ |
|||
GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ |
|||
|
|||
/* Round 3 */ |
|||
#define S31 4 |
|||
#define S32 11 |
|||
#define S33 16 |
|||
#define S34 23 |
|||
HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */ |
|||
HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */ |
|||
HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ |
|||
HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ |
|||
HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */ |
|||
HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */ |
|||
HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */ |
|||
HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ |
|||
HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ |
|||
HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */ |
|||
HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */ |
|||
HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */ |
|||
HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */ |
|||
HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ |
|||
HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ |
|||
HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */ |
|||
|
|||
/* Round 4 */ |
|||
#define S41 6 |
|||
#define S42 10 |
|||
#define S43 15 |
|||
#define S44 21 |
|||
II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */ |
|||
II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */ |
|||
II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ |
|||
II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */ |
|||
II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ |
|||
II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */ |
|||
II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ |
|||
II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */ |
|||
II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */ |
|||
II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ |
|||
II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */ |
|||
II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ |
|||
II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */ |
|||
II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ |
|||
II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */ |
|||
II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */ |
|||
|
|||
state[0] += a; |
|||
state[1] += b; |
|||
state[2] += c; |
|||
state[3] += d; |
|||
|
|||
/* Zeroize sensitive information. */ |
|||
memset ((void *)x, 0, sizeof (x)); |
|||
} |
|||
@ -0,0 +1,53 @@ |
|||
/* MD5.H - header file for MD5C.C |
|||
* $FreeBSD: src/sys/sys/md5.h,v 1.20 2006/03/15 19:47:12 andre Exp $ |
|||
*/ |
|||
|
|||
/*- |
|||
Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All |
|||
rights reserved. |
|||
|
|||
License to copy and use this software is granted provided that it |
|||
is identified as the "RSA Data Security, Inc. MD5 Message-Digest |
|||
Algorithm" in all material mentioning or referencing this software |
|||
or this function. |
|||
|
|||
License is also granted to make and use derivative works provided |
|||
that such works are identified as "derived from the RSA Data |
|||
Security, Inc. MD5 Message-Digest Algorithm" in all material |
|||
mentioning or referencing the derived work. |
|||
|
|||
RSA Data Security, Inc. makes no representations concerning either |
|||
the merchantability of this software or the suitability of this |
|||
software for any particular purpose. It is provided "as is" |
|||
without express or implied warranty of any kind. |
|||
|
|||
These notices must be retained in any copies of any part of this |
|||
documentation and/or software. |
|||
*/ |
|||
|
|||
#ifndef _SYS_MD5_H_ |
|||
#define _SYS_MD5_H_ |
|||
|
|||
#define MD5_BLOCK_LENGTH 64 |
|||
#define MD5_DIGEST_LENGTH 16 |
|||
#define MD5_DIGEST_STRING_LENGTH (MD5_DIGEST_LENGTH * 2 + 1) |
|||
|
|||
/* MD5 context. */ |
|||
typedef struct MD5Context { |
|||
u_int32_t state[4]; /* state (ABCD) */ |
|||
u_int32_t count[2]; /* number of bits, modulo 2^64 (lsb first) */ |
|||
unsigned char buffer[64]; /* input buffer */ |
|||
} MD5_CTX; |
|||
|
|||
#include <sys/cdefs.h> |
|||
|
|||
__BEGIN_DECLS |
|||
void MD5Init (MD5_CTX *); |
|||
void MD5Update (MD5_CTX *, const void *, unsigned int); |
|||
void MD5Final (unsigned char [16], MD5_CTX *); |
|||
char * MD5End(MD5_CTX *, char *); |
|||
char * MD5File(const char *, char *); |
|||
char * MD5FileChunk(const char *, char *, off_t, off_t); |
|||
char * MD5Data(const void *, unsigned int, char *); |
|||
__END_DECLS |
|||
#endif /* _SYS_MD5_H_ */ |
|||
@ -0,0 +1,618 @@ |
|||
/*- |
|||
* Copyright (c) 1991, 1993 |
|||
* The Regents of the University of California. 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. |
|||
* 4. Neither the name of the University nor the names of its contributors |
|||
* may be used to endorse or promote products derived from this software |
|||
* without specific prior written permission. |
|||
* |
|||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
|||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
|||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
|||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
|||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
|||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
|||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
|||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
|||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
|||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
|||
* SUCH DAMAGE. |
|||
* |
|||
* @(#)queue.h 8.5 (Berkeley) 8/20/94 |
|||
* $FreeBSD: src/sys/sys/queue.h,v 1.68 2006/10/24 11:20:29 ru Exp $ |
|||
*/ |
|||
|
|||
#ifndef _SYS_QUEUE_H_ |
|||
#define _SYS_QUEUE_H_ |
|||
|
|||
#include <sys/cdefs.h> |
|||
|
|||
/* |
|||
* This file defines four types of data structures: singly-linked lists, |
|||
* singly-linked tail queues, lists and tail queues. |
|||
* |
|||
* A singly-linked list is headed by a single forward pointer. The elements |
|||
* are singly linked for minimum space and pointer manipulation overhead at |
|||
* the expense of O(n) removal for arbitrary elements. New elements can be |
|||
* added to the list after an existing element or at the head of the list. |
|||
* Elements being removed from the head of the list should use the explicit |
|||
* macro for this purpose for optimum efficiency. A singly-linked list may |
|||
* only be traversed in the forward direction. Singly-linked lists are ideal |
|||
* for applications with large datasets and few or no removals or for |
|||
* implementing a LIFO queue. |
|||
* |
|||
* A singly-linked tail queue is headed by a pair of pointers, one to the |
|||
* head of the list and the other to the tail of the list. The elements are |
|||
* singly linked for minimum space and pointer manipulation overhead at the |
|||
* expense of O(n) removal for arbitrary elements. New elements can be added |
|||
* to the list after an existing element, at the head of the list, or at the |
|||
* end of the list. Elements being removed from the head of the tail queue |
|||
* should use the explicit macro for this purpose for optimum efficiency. |
|||
* A singly-linked tail queue may only be traversed in the forward direction. |
|||
* Singly-linked tail queues are ideal for applications with large datasets |
|||
* and few or no removals or for implementing a FIFO queue. |
|||
* |
|||
* A list is headed by a single forward pointer (or an array of forward |
|||
* pointers for a hash table header). The elements are doubly linked |
|||
* so that an arbitrary element can be removed without a need to |
|||
* traverse the list. New elements can be added to the list before |
|||
* or after an existing element or at the head of the list. A list |
|||
* may only be traversed in the forward direction. |
|||
* |
|||
* A tail queue is headed by a pair of pointers, one to the head of the |
|||
* list and the other to the tail of the list. The elements are doubly |
|||
* linked so that an arbitrary element can be removed without a need to |
|||
* traverse the list. New elements can be added to the list before or |
|||
* after an existing element, at the head of the list, or at the end of |
|||
* the list. A tail queue may be traversed in either direction. |
|||
* |
|||
* For details on the use of these macros, see the queue(3) manual page. |
|||
* |
|||
* |
|||
* SLIST LIST STAILQ TAILQ |
|||
* _HEAD + + + + |
|||
* _HEAD_INITIALIZER + + + + |
|||
* _ENTRY + + + + |
|||
* _INIT + + + + |
|||
* _EMPTY + + + + |
|||
* _FIRST + + + + |
|||
* _NEXT + + + + |
|||
* _PREV - - - + |
|||
* _LAST - - + + |
|||
* _FOREACH + + + + |
|||
* _FOREACH_SAFE + + + + |
|||
* _FOREACH_REVERSE - - - + |
|||
* _FOREACH_REVERSE_SAFE - - - + |
|||
* _INSERT_HEAD + + + + |
|||
* _INSERT_BEFORE - + - + |
|||
* _INSERT_AFTER + + + + |
|||
* _INSERT_TAIL - - + + |
|||
* _CONCAT - - + + |
|||
* _REMOVE_HEAD + - + - |
|||
* _REMOVE + + + + |
|||
* |
|||
*/ |
|||
#ifdef QUEUE_MACRO_DEBUG |
|||
/* Store the last 2 places the queue element or head was altered */ |
|||
struct qm_trace { |
|||
char * lastfile; |
|||
int lastline; |
|||
char * prevfile; |
|||
int prevline; |
|||
}; |
|||
|
|||
#define TRACEBUF struct qm_trace trace; |
|||
#define TRASHIT(x) do {(x) = (void *)-1;} while (0) |
|||
|
|||
#define QMD_TRACE_HEAD(head) do { \ |
|||
(head)->trace.prevline = (head)->trace.lastline; \ |
|||
(head)->trace.prevfile = (head)->trace.lastfile; \ |
|||
(head)->trace.lastline = __LINE__; \ |
|||
(head)->trace.lastfile = __FILE__; \ |
|||
} while (0) |
|||
|
|||
#define QMD_TRACE_ELEM(elem) do { \ |
|||
(elem)->trace.prevline = (elem)->trace.lastline; \ |
|||
(elem)->trace.prevfile = (elem)->trace.lastfile; \ |
|||
(elem)->trace.lastline = __LINE__; \ |
|||
(elem)->trace.lastfile = __FILE__; \ |
|||
} while (0) |
|||
|
|||
#else |
|||
#define QMD_TRACE_ELEM(elem) |
|||
#define QMD_TRACE_HEAD(head) |
|||
#define TRACEBUF |
|||
#define TRASHIT(x) |
|||
#endif /* QUEUE_MACRO_DEBUG */ |
|||
|
|||
/* |
|||
* Singly-linked List declarations. |
|||
*/ |
|||
#define SLIST_HEAD(name, type) \ |
|||
struct name { \ |
|||
struct type *slh_first; /* first element */ \ |
|||
} |
|||
|
|||
#define SLIST_HEAD_INITIALIZER(head) \ |
|||
{ NULL } |
|||
|
|||
#define SLIST_ENTRY(type) \ |
|||
struct { \ |
|||
struct type *sle_next; /* next element */ \ |
|||
} |
|||
|
|||
/* |
|||
* Singly-linked List functions. |
|||
*/ |
|||
#define SLIST_EMPTY(head) ((head)->slh_first == NULL) |
|||
|
|||
#define SLIST_FIRST(head) ((head)->slh_first) |
|||
|
|||
#define SLIST_FOREACH(var, head, field) \ |
|||
for ((var) = SLIST_FIRST((head)); \ |
|||
(var); \ |
|||
(var) = SLIST_NEXT((var), field)) |
|||
|
|||
#define SLIST_FOREACH_SAFE(var, head, field, tvar) \ |
|||
for ((var) = SLIST_FIRST((head)); \ |
|||
(var) && ((tvar) = SLIST_NEXT((var), field), 1); \ |
|||
(var) = (tvar)) |
|||
|
|||
#define SLIST_FOREACH_PREVPTR(var, varp, head, field) \ |
|||
for ((varp) = &SLIST_FIRST((head)); \ |
|||
((var) = *(varp)) != NULL; \ |
|||
(varp) = &SLIST_NEXT((var), field)) |
|||
|
|||
#define SLIST_INIT(head) do { \ |
|||
SLIST_FIRST((head)) = NULL; \ |
|||
} while (0) |
|||
|
|||
#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \ |
|||
SLIST_NEXT((elm), field) = SLIST_NEXT((slistelm), field); \ |
|||
SLIST_NEXT((slistelm), field) = (elm); \ |
|||
} while (0) |
|||
|
|||
#define SLIST_INSERT_HEAD(head, elm, field) do { \ |
|||
SLIST_NEXT((elm), field) = SLIST_FIRST((head)); \ |
|||
SLIST_FIRST((head)) = (elm); \ |
|||
} while (0) |
|||
|
|||
#define SLIST_NEXT(elm, field) ((elm)->field.sle_next) |
|||
|
|||
#define SLIST_REMOVE(head, elm, type, field) do { \ |
|||
if (SLIST_FIRST((head)) == (elm)) { \ |
|||
SLIST_REMOVE_HEAD((head), field); \ |
|||
} \ |
|||
else { \ |
|||
struct type *curelm = SLIST_FIRST((head)); \ |
|||
while (SLIST_NEXT(curelm, field) != (elm)) \ |
|||
curelm = SLIST_NEXT(curelm, field); \ |
|||
SLIST_NEXT(curelm, field) = \ |
|||
SLIST_NEXT(SLIST_NEXT(curelm, field), field); \ |
|||
} \ |
|||
TRASHIT((elm)->field.sle_next); \ |
|||
} while (0) |
|||
|
|||
#define SLIST_REMOVE_HEAD(head, field) do { \ |
|||
SLIST_FIRST((head)) = SLIST_NEXT(SLIST_FIRST((head)), field); \ |
|||
} while (0) |
|||
|
|||
/* |
|||
* Singly-linked Tail queue declarations. |
|||
*/ |
|||
#define STAILQ_HEAD(name, type) \ |
|||
struct name { \ |
|||
struct type *stqh_first;/* first element */ \ |
|||
struct type **stqh_last;/* addr of last next element */ \ |
|||
} |
|||
|
|||
#define STAILQ_HEAD_INITIALIZER(head) \ |
|||
{ NULL, &(head).stqh_first } |
|||
|
|||
#define STAILQ_ENTRY(type) \ |
|||
struct { \ |
|||
struct type *stqe_next; /* next element */ \ |
|||
} |
|||
|
|||
/* |
|||
* Singly-linked Tail queue functions. |
|||
*/ |
|||
#define STAILQ_CONCAT(head1, head2) do { \ |
|||
if (!STAILQ_EMPTY((head2))) { \ |
|||
*(head1)->stqh_last = (head2)->stqh_first; \ |
|||
(head1)->stqh_last = (head2)->stqh_last; \ |
|||
STAILQ_INIT((head2)); \ |
|||
} \ |
|||
} while (0) |
|||
|
|||
#define STAILQ_EMPTY(head) ((head)->stqh_first == NULL) |
|||
|
|||
#define STAILQ_FIRST(head) ((head)->stqh_first) |
|||
|
|||
#define STAILQ_FOREACH(var, head, field) \ |
|||
for((var) = STAILQ_FIRST((head)); \ |
|||
(var); \ |
|||
(var) = STAILQ_NEXT((var), field)) |
|||
|
|||
|
|||
#define STAILQ_FOREACH_SAFE(var, head, field, tvar) \ |
|||
for ((var) = STAILQ_FIRST((head)); \ |
|||
(var) && ((tvar) = STAILQ_NEXT((var), field), 1); \ |
|||
(var) = (tvar)) |
|||
|
|||
#define STAILQ_INIT(head) do { \ |
|||
STAILQ_FIRST((head)) = NULL; \ |
|||
(head)->stqh_last = &STAILQ_FIRST((head)); \ |
|||
} while (0) |
|||
|
|||
#define STAILQ_INSERT_AFTER(head, tqelm, elm, field) do { \ |
|||
if ((STAILQ_NEXT((elm), field) = STAILQ_NEXT((tqelm), field)) == NULL)\ |
|||
(head)->stqh_last = &STAILQ_NEXT((elm), field); \ |
|||
STAILQ_NEXT((tqelm), field) = (elm); \ |
|||
} while (0) |
|||
|
|||
#define STAILQ_INSERT_HEAD(head, elm, field) do { \ |
|||
if ((STAILQ_NEXT((elm), field) = STAILQ_FIRST((head))) == NULL) \ |
|||
(head)->stqh_last = &STAILQ_NEXT((elm), field); \ |
|||
STAILQ_FIRST((head)) = (elm); \ |
|||
} while (0) |
|||
|
|||
#define STAILQ_INSERT_TAIL(head, elm, field) do { \ |
|||
STAILQ_NEXT((elm), field) = NULL; \ |
|||
*(head)->stqh_last = (elm); \ |
|||
(head)->stqh_last = &STAILQ_NEXT((elm), field); \ |
|||
} while (0) |
|||
|
|||
#define STAILQ_LAST(head, type, field) \ |
|||
(STAILQ_EMPTY((head)) ? \ |
|||
NULL : \ |
|||
((struct type *)(void *) \ |
|||
((char *)((head)->stqh_last) - __offsetof(struct type, field)))) |
|||
|
|||
#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next) |
|||
|
|||
#define STAILQ_REMOVE(head, elm, type, field) do { \ |
|||
if (STAILQ_FIRST((head)) == (elm)) { \ |
|||
STAILQ_REMOVE_HEAD((head), field); \ |
|||
} \ |
|||
else { \ |
|||
struct type *curelm = STAILQ_FIRST((head)); \ |
|||
while (STAILQ_NEXT(curelm, field) != (elm)) \ |
|||
curelm = STAILQ_NEXT(curelm, field); \ |
|||
if ((STAILQ_NEXT(curelm, field) = \ |
|||
STAILQ_NEXT(STAILQ_NEXT(curelm, field), field)) == NULL)\ |
|||
(head)->stqh_last = &STAILQ_NEXT((curelm), field);\ |
|||
} \ |
|||
TRASHIT((elm)->field.stqe_next); \ |
|||
} while (0) |
|||
|
|||
#define STAILQ_REMOVE_HEAD(head, field) do { \ |
|||
if ((STAILQ_FIRST((head)) = \ |
|||
STAILQ_NEXT(STAILQ_FIRST((head)), field)) == NULL) \ |
|||
(head)->stqh_last = &STAILQ_FIRST((head)); \ |
|||
} while (0) |
|||
|
|||
/* |
|||
* List declarations. |
|||
*/ |
|||
#define LIST_HEAD(name, type) \ |
|||
struct name { \ |
|||
struct type *lh_first; /* first element */ \ |
|||
} |
|||
|
|||
#define LIST_HEAD_INITIALIZER(head) \ |
|||
{ NULL } |
|||
|
|||
#define LIST_ENTRY(type) \ |
|||
struct { \ |
|||
struct type *le_next; /* next element */ \ |
|||
struct type **le_prev; /* address of previous next element */ \ |
|||
} |
|||
|
|||
/* |
|||
* List functions. |
|||
*/ |
|||
|
|||
#if (defined(_KERNEL) && defined(INVARIANTS)) |
|||
#define QMD_LIST_CHECK_HEAD(head, field) do { \ |
|||
if (LIST_FIRST((head)) != NULL && \ |
|||
LIST_FIRST((head))->field.le_prev != \ |
|||
&LIST_FIRST((head))) \ |
|||
panic("Bad list head %p first->prev != head", (head)); \ |
|||
} while (0) |
|||
|
|||
#define QMD_LIST_CHECK_NEXT(elm, field) do { \ |
|||
if (LIST_NEXT((elm), field) != NULL && \ |
|||
LIST_NEXT((elm), field)->field.le_prev != \ |
|||
&((elm)->field.le_next)) \ |
|||
panic("Bad link elm %p next->prev != elm", (elm)); \ |
|||
} while (0) |
|||
|
|||
#define QMD_LIST_CHECK_PREV(elm, field) do { \ |
|||
if (*(elm)->field.le_prev != (elm)) \ |
|||
panic("Bad link elm %p prev->next != elm", (elm)); \ |
|||
} while (0) |
|||
#else |
|||
#define QMD_LIST_CHECK_HEAD(head, field) |
|||
#define QMD_LIST_CHECK_NEXT(elm, field) |
|||
#define QMD_LIST_CHECK_PREV(elm, field) |
|||
#endif /* (_KERNEL && INVARIANTS) */ |
|||
|
|||
#define LIST_EMPTY(head) ((head)->lh_first == NULL) |
|||
|
|||
#define LIST_FIRST(head) ((head)->lh_first) |
|||
|
|||
#define LIST_FOREACH(var, head, field) \ |
|||
for ((var) = LIST_FIRST((head)); \ |
|||
(var); \ |
|||
(var) = LIST_NEXT((var), field)) |
|||
|
|||
#define LIST_FOREACH_SAFE(var, head, field, tvar) \ |
|||
for ((var) = LIST_FIRST((head)); \ |
|||
(var) && ((tvar) = LIST_NEXT((var), field), 1); \ |
|||
(var) = (tvar)) |
|||
|
|||
#define LIST_INIT(head) do { \ |
|||
LIST_FIRST((head)) = NULL; \ |
|||
} while (0) |
|||
|
|||
#define LIST_INSERT_AFTER(listelm, elm, field) do { \ |
|||
QMD_LIST_CHECK_NEXT(listelm, field); \ |
|||
if ((LIST_NEXT((elm), field) = LIST_NEXT((listelm), field)) != NULL)\ |
|||
LIST_NEXT((listelm), field)->field.le_prev = \ |
|||
&LIST_NEXT((elm), field); \ |
|||
LIST_NEXT((listelm), field) = (elm); \ |
|||
(elm)->field.le_prev = &LIST_NEXT((listelm), field); \ |
|||
} while (0) |
|||
|
|||
#define LIST_INSERT_BEFORE(listelm, elm, field) do { \ |
|||
QMD_LIST_CHECK_PREV(listelm, field); \ |
|||
(elm)->field.le_prev = (listelm)->field.le_prev; \ |
|||
LIST_NEXT((elm), field) = (listelm); \ |
|||
*(listelm)->field.le_prev = (elm); \ |
|||
(listelm)->field.le_prev = &LIST_NEXT((elm), field); \ |
|||
} while (0) |
|||
|
|||
#define LIST_INSERT_HEAD(head, elm, field) do { \ |
|||
QMD_LIST_CHECK_HEAD((head), field); \ |
|||
if ((LIST_NEXT((elm), field) = LIST_FIRST((head))) != NULL) \ |
|||
LIST_FIRST((head))->field.le_prev = &LIST_NEXT((elm), field);\ |
|||
LIST_FIRST((head)) = (elm); \ |
|||
(elm)->field.le_prev = &LIST_FIRST((head)); \ |
|||
} while (0) |
|||
|
|||
#define LIST_NEXT(elm, field) ((elm)->field.le_next) |
|||
|
|||
#define LIST_REMOVE(elm, field) do { \ |
|||
QMD_LIST_CHECK_NEXT(elm, field); \ |
|||
QMD_LIST_CHECK_PREV(elm, field); \ |
|||
if (LIST_NEXT((elm), field) != NULL) \ |
|||
LIST_NEXT((elm), field)->field.le_prev = \ |
|||
(elm)->field.le_prev; \ |
|||
*(elm)->field.le_prev = LIST_NEXT((elm), field); \ |
|||
TRASHIT((elm)->field.le_next); \ |
|||
TRASHIT((elm)->field.le_prev); \ |
|||
} while (0) |
|||
|
|||
/* |
|||
* Tail queue declarations. |
|||
*/ |
|||
#define TAILQ_HEAD(name, type) \ |
|||
struct name { \ |
|||
struct type *tqh_first; /* first element */ \ |
|||
struct type **tqh_last; /* addr of last next element */ \ |
|||
TRACEBUF \ |
|||
} |
|||
|
|||
#define TAILQ_HEAD_INITIALIZER(head) \ |
|||
{ NULL, &(head).tqh_first } |
|||
|
|||
#define TAILQ_ENTRY(type) \ |
|||
struct { \ |
|||
struct type *tqe_next; /* next element */ \ |
|||
struct type **tqe_prev; /* address of previous next element */ \ |
|||
TRACEBUF \ |
|||
} |
|||
|
|||
/* |
|||
* Tail queue functions. |
|||
*/ |
|||
#if (defined(_KERNEL) && defined(INVARIANTS)) |
|||
#define QMD_TAILQ_CHECK_HEAD(head, field) do { \ |
|||
if (!TAILQ_EMPTY(head) && \ |
|||
TAILQ_FIRST((head))->field.tqe_prev != \ |
|||
&TAILQ_FIRST((head))) \ |
|||
panic("Bad tailq head %p first->prev != head", (head)); \ |
|||
} while (0) |
|||
|
|||
#define QMD_TAILQ_CHECK_TAIL(head, field) do { \ |
|||
if (*(head)->tqh_last != NULL) \ |
|||
panic("Bad tailq NEXT(%p->tqh_last) != NULL", (head)); \ |
|||
} while (0) |
|||
|
|||
#define QMD_TAILQ_CHECK_NEXT(elm, field) do { \ |
|||
if (TAILQ_NEXT((elm), field) != NULL && \ |
|||
TAILQ_NEXT((elm), field)->field.tqe_prev != \ |
|||
&((elm)->field.tqe_next)) \ |
|||
panic("Bad link elm %p next->prev != elm", (elm)); \ |
|||
} while (0) |
|||
|
|||
#define QMD_TAILQ_CHECK_PREV(elm, field) do { \ |
|||
if (*(elm)->field.tqe_prev != (elm)) \ |
|||
panic("Bad link elm %p prev->next != elm", (elm)); \ |
|||
} while (0) |
|||
#else |
|||
#define QMD_TAILQ_CHECK_HEAD(head, field) |
|||
#define QMD_TAILQ_CHECK_TAIL(head, headname) |
|||
#define QMD_TAILQ_CHECK_NEXT(elm, field) |
|||
#define QMD_TAILQ_CHECK_PREV(elm, field) |
|||
#endif /* (_KERNEL && INVARIANTS) */ |
|||
|
|||
#define TAILQ_CONCAT(head1, head2, field) do { \ |
|||
if (!TAILQ_EMPTY(head2)) { \ |
|||
*(head1)->tqh_last = (head2)->tqh_first; \ |
|||
(head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \ |
|||
(head1)->tqh_last = (head2)->tqh_last; \ |
|||
TAILQ_INIT((head2)); \ |
|||
QMD_TRACE_HEAD(head1); \ |
|||
QMD_TRACE_HEAD(head2); \ |
|||
} \ |
|||
} while (0) |
|||
|
|||
#define TAILQ_EMPTY(head) ((head)->tqh_first == NULL) |
|||
|
|||
#define TAILQ_FIRST(head) ((head)->tqh_first) |
|||
|
|||
#define TAILQ_FOREACH(var, head, field) \ |
|||
for ((var) = TAILQ_FIRST((head)); \ |
|||
(var); \ |
|||
(var) = TAILQ_NEXT((var), field)) |
|||
|
|||
#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \ |
|||
for ((var) = TAILQ_FIRST((head)); \ |
|||
(var) && ((tvar) = TAILQ_NEXT((var), field), 1); \ |
|||
(var) = (tvar)) |
|||
|
|||
#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \ |
|||
for ((var) = TAILQ_LAST((head), headname); \ |
|||
(var); \ |
|||
(var) = TAILQ_PREV((var), headname, field)) |
|||
|
|||
#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \ |
|||
for ((var) = TAILQ_LAST((head), headname); \ |
|||
(var) && ((tvar) = TAILQ_PREV((var), headname, field), 1); \ |
|||
(var) = (tvar)) |
|||
|
|||
#define TAILQ_INIT(head) do { \ |
|||
TAILQ_FIRST((head)) = NULL; \ |
|||
(head)->tqh_last = &TAILQ_FIRST((head)); \ |
|||
QMD_TRACE_HEAD(head); \ |
|||
} while (0) |
|||
|
|||
#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ |
|||
QMD_TAILQ_CHECK_NEXT(listelm, field); \ |
|||
if ((TAILQ_NEXT((elm), field) = TAILQ_NEXT((listelm), field)) != NULL)\ |
|||
TAILQ_NEXT((elm), field)->field.tqe_prev = \ |
|||
&TAILQ_NEXT((elm), field); \ |
|||
else { \ |
|||
(head)->tqh_last = &TAILQ_NEXT((elm), field); \ |
|||
QMD_TRACE_HEAD(head); \ |
|||
} \ |
|||
TAILQ_NEXT((listelm), field) = (elm); \ |
|||
(elm)->field.tqe_prev = &TAILQ_NEXT((listelm), field); \ |
|||
QMD_TRACE_ELEM(&(elm)->field); \ |
|||
QMD_TRACE_ELEM(&listelm->field); \ |
|||
} while (0) |
|||
|
|||
#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ |
|||
QMD_TAILQ_CHECK_PREV(listelm, field); \ |
|||
(elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ |
|||
TAILQ_NEXT((elm), field) = (listelm); \ |
|||
*(listelm)->field.tqe_prev = (elm); \ |
|||
(listelm)->field.tqe_prev = &TAILQ_NEXT((elm), field); \ |
|||
QMD_TRACE_ELEM(&(elm)->field); \ |
|||
QMD_TRACE_ELEM(&listelm->field); \ |
|||
} while (0) |
|||
|
|||
#define TAILQ_INSERT_HEAD(head, elm, field) do { \ |
|||
QMD_TAILQ_CHECK_HEAD(head, field); \ |
|||
if ((TAILQ_NEXT((elm), field) = TAILQ_FIRST((head))) != NULL) \ |
|||
TAILQ_FIRST((head))->field.tqe_prev = \ |
|||
&TAILQ_NEXT((elm), field); \ |
|||
else \ |
|||
(head)->tqh_last = &TAILQ_NEXT((elm), field); \ |
|||
TAILQ_FIRST((head)) = (elm); \ |
|||
(elm)->field.tqe_prev = &TAILQ_FIRST((head)); \ |
|||
QMD_TRACE_HEAD(head); \ |
|||
QMD_TRACE_ELEM(&(elm)->field); \ |
|||
} while (0) |
|||
|
|||
#define TAILQ_INSERT_TAIL(head, elm, field) do { \ |
|||
QMD_TAILQ_CHECK_TAIL(head, field); \ |
|||
TAILQ_NEXT((elm), field) = NULL; \ |
|||
(elm)->field.tqe_prev = (head)->tqh_last; \ |
|||
*(head)->tqh_last = (elm); \ |
|||
(head)->tqh_last = &TAILQ_NEXT((elm), field); \ |
|||
QMD_TRACE_HEAD(head); \ |
|||
QMD_TRACE_ELEM(&(elm)->field); \ |
|||
} while (0) |
|||
|
|||
#define TAILQ_LAST(head, headname) \ |
|||
(*(((struct headname *)((head)->tqh_last))->tqh_last)) |
|||
|
|||
#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) |
|||
|
|||
#define TAILQ_PREV(elm, headname, field) \ |
|||
(*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) |
|||
|
|||
#define TAILQ_REMOVE(head, elm, field) do { \ |
|||
QMD_TAILQ_CHECK_NEXT(elm, field); \ |
|||
QMD_TAILQ_CHECK_PREV(elm, field); \ |
|||
if ((TAILQ_NEXT((elm), field)) != NULL) \ |
|||
TAILQ_NEXT((elm), field)->field.tqe_prev = \ |
|||
(elm)->field.tqe_prev; \ |
|||
else { \ |
|||
(head)->tqh_last = (elm)->field.tqe_prev; \ |
|||
QMD_TRACE_HEAD(head); \ |
|||
} \ |
|||
*(elm)->field.tqe_prev = TAILQ_NEXT((elm), field); \ |
|||
TRASHIT((elm)->field.tqe_next); \ |
|||
TRASHIT((elm)->field.tqe_prev); \ |
|||
QMD_TRACE_ELEM(&(elm)->field); \ |
|||
} while (0) |
|||
|
|||
|
|||
#ifdef _KERNEL |
|||
|
|||
/* |
|||
* XXX insque() and remque() are an old way of handling certain queues. |
|||
* They bogusly assumes that all queue heads look alike. |
|||
*/ |
|||
|
|||
struct quehead { |
|||
struct quehead *qh_link; |
|||
struct quehead *qh_rlink; |
|||
}; |
|||
|
|||
#ifdef __CC_SUPPORTS___INLINE |
|||
|
|||
static __inline void |
|||
insque(void *a, void *b) |
|||
{ |
|||
struct quehead *element = (struct quehead *)a, |
|||
*head = (struct quehead *)b; |
|||
|
|||
element->qh_link = head->qh_link; |
|||
element->qh_rlink = head; |
|||
head->qh_link = element; |
|||
element->qh_link->qh_rlink = element; |
|||
} |
|||
|
|||
static __inline void |
|||
remque(void *a) |
|||
{ |
|||
struct quehead *element = (struct quehead *)a; |
|||
|
|||
element->qh_link->qh_rlink = element->qh_rlink; |
|||
element->qh_rlink->qh_link = element->qh_link; |
|||
element->qh_rlink = 0; |
|||
} |
|||
|
|||
#else /* !__CC_SUPPORTS___INLINE */ |
|||
|
|||
void insque(void *a, void *b); |
|||
void remque(void *a); |
|||
|
|||
#endif /* __CC_SUPPORTS___INLINE */ |
|||
|
|||
#endif /* _KERNEL */ |
|||
|
|||
#endif /* !_SYS_QUEUE_H_ */ |
|||
@ -0,0 +1,70 @@ |
|||
/* $OpenBSD: strlcpy.c,v 1.4 1999/05/01 18:56:41 millert Exp $ */ |
|||
|
|||
/* |
|||
* Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com> |
|||
* 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 of the author may not be used to endorse or promote products |
|||
* derived from this software without specific prior written permission. |
|||
* |
|||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, |
|||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY |
|||
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL |
|||
* THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
|||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
|||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; |
|||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
|||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR |
|||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF |
|||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|||
*/ |
|||
|
|||
#if defined(LIBC_SCCS) && !defined(lint) |
|||
static char *rcsid = "$OpenBSD: strlcpy.c,v 1.4 1999/05/01 18:56:41 millert Exp $"; |
|||
#endif /* LIBC_SCCS and not lint */ |
|||
#include <sys/cdefs.h> |
|||
|
|||
#include <sys/types.h> |
|||
#include <string.h> |
|||
|
|||
/* |
|||
* Copy src to string dst of size siz. At most siz-1 characters |
|||
* will be copied. Always NUL terminates (unless siz == 0). |
|||
* Returns strlen(src); if retval >= siz, truncation occurred. |
|||
*/ |
|||
size_t |
|||
strlcpy(dst, src, siz) |
|||
char *dst; |
|||
const char *src; |
|||
size_t siz; |
|||
{ |
|||
char *d = dst; |
|||
const char *s = src; |
|||
size_t n = siz; |
|||
|
|||
/* Copy as many bytes as will fit */ |
|||
if (n != 0 && --n != 0) { |
|||
do { |
|||
if ((*d++ = *s++) == 0) |
|||
break; |
|||
} while (--n != 0); |
|||
} |
|||
|
|||
/* Not enough room in dst, add NUL and traverse rest of src */ |
|||
if (n == 0) { |
|||
if (siz != 0) |
|||
*d = '\0'; /* NUL-terminate dst */ |
|||
while (*s++) |
|||
; |
|||
} |
|||
|
|||
return(s - src - 1); /* count does not include NUL */ |
|||
} |
|||
@ -0,0 +1,8 @@ |
|||
#ifndef STRLCPY_H |
|||
#define STRLCPY_H |
|||
|
|||
#include <sys/types.h> |
|||
|
|||
size_t strlcpy(char *, const char*, size_t); |
|||
|
|||
#endif |
|||
@ -0,0 +1,516 @@ |
|||
#!/bin/sh |
|||
|
|||
GCC="gcc" |
|||
MAKE="" |
|||
LEX="" |
|||
YACC="" |
|||
OS="" |
|||
|
|||
LOCALBASE=/usr/local |
|||
PREFIX=$LOCALBASE |
|||
|
|||
VERSION=0.0.1 |
|||
COMPAT_DIR="./compat" |
|||
|
|||
YACC_SRC="cfg_file.y" |
|||
LEX_SRC="cfg_file.l" |
|||
YACC_OUTPUT="cfg_yacc.c" |
|||
LEX_OUTPUT="cfg_lex.c" |
|||
|
|||
SOURCES="upstream.c cfg_utils.c memcached.c main.c util.c worker.c ${LEX_OUTPUT} ${YACC_OUTPUT}" |
|||
|
|||
CFLAGS="$CFLAGS -W -Wall -Wpointer-arith -Wno-unused-parameter" |
|||
CFLAGS="$CFLAGS -Wno-unused-function -Wunused-variable -Wno-sign-compare" |
|||
CFLAGS="$CFLAGS -Wunused-value -ggdb -I${LOCALBASE}/include" |
|||
CFLAGS="$CFLAGS -DRVERSION=\\\"${VERSION}\\\" -DHASH_COMPAT" |
|||
LDFLAGS="$LDFLAGS -L/usr/lib -L${LOCALBASE}/lib" |
|||
OPT_FLAGS="-O -pipe -fno-omit-frame-pointer" |
|||
DEPS="cfg_file.h memcached.h util.h main.h upstream.h ${LEX_OUTPUT} ${YACC_OUTPUT}" |
|||
EXEC=rspamd |
|||
USER=postfix |
|||
GROUP=postfix |
|||
INSTALL="/usr/bin/install -v" |
|||
MKDIR="/usr/bin/install -v -d" |
|||
MANPATH="${PREFIX}/share/man" |
|||
|
|||
MAKEFILE="Makefile" |
|||
MAKEFILE_IN="Makefile.in" |
|||
|
|||
TARGETS="${EXEC}" |
|||
|
|||
cleanup() |
|||
{ |
|||
rm -f autotest.c |
|||
rm -f autotest |
|||
INCLUDE="" |
|||
} |
|||
|
|||
check_compiler() |
|||
{ |
|||
GCC=`PATH="$PATH:$PREFIX/bin:$LOCALBASE/bin" which gcc` |
|||
echo -n "Testing for gcc: " |
|||
if [ -x $GCC ] ; then |
|||
echo "found -> $GCC" |
|||
return 0 |
|||
else |
|||
echo "not found" |
|||
exit 1 |
|||
fi |
|||
} |
|||
|
|||
check_make() |
|||
{ |
|||
MAKE=`PATH="$PATH:$PREFIX/bin:$LOCALBASE/bin" which make` |
|||
echo -n "Testing for make: " |
|||
if [ -x $MAKE ] ; then |
|||
echo "found -> $MAKE" |
|||
return 0 |
|||
else |
|||
echo "not found" |
|||
exit 1 |
|||
fi |
|||
|
|||
} |
|||
|
|||
check_lex() |
|||
{ |
|||
LEX=`PATH="$PATH:$PREFIX/bin:$LOCALBASE/bin" which lex` |
|||
echo -n "Testing for lex: " |
|||
if [ -x $LEX ] ; then |
|||
echo "found -> $LEX" |
|||
return 0 |
|||
else |
|||
echo "not found" |
|||
exit 1 |
|||
fi |
|||
|
|||
} |
|||
|
|||
check_yacc() |
|||
{ |
|||
YACC=`PATH="$PATH:$PREFIX/bin:$LOCALBASE/bin" which yacc` |
|||
echo -n "Testing for yacc: " |
|||
if [ -x $YACC ] ; then |
|||
echo "found -> $YACC" |
|||
return 0 |
|||
else |
|||
echo "not found" |
|||
exit 1 |
|||
fi |
|||
|
|||
} |
|||
|
|||
check_function() |
|||
{ |
|||
FUNCTION=$1 |
|||
while [ $# -ne 0 -a -n $2 ] ; do |
|||
shift |
|||
if [ "F$INCLUDE" = "F" ] ; then |
|||
INCLUDE="$1" |
|||
else |
|||
INCLUDE="$INCLUDE $1" |
|||
fi |
|||
done |
|||
echo -n "Testing for $FUNCTION: " |
|||
echo >> config.log |
|||
echo "Testing for $FUNCTION: " >> config.log |
|||
echo "#include <sys/types.h>" > autotest.c |
|||
if [ "F$INCLUDE" != "F" ] ; then |
|||
for inc in $INCLUDE ; do |
|||
echo "#include \"$inc\"" >> autotest.c |
|||
done |
|||
fi |
|||
echo "#include <stdlib.h>" >> autotest.c |
|||
echo "int main (int argc, char **argv) { $FUNCTION; return 0; }" >> autotest.c |
|||
echo "$GCC $PTHREAD_CFLAGS $CFLAGS -o autotest $LDFLAGS $LIBS $PTHREAD_LDFLAGS autotest.c" >>config.log |
|||
$GCC $PTHREAD_CFLAGS $CFLAGS -o autotest $LDFLAGS $LIBS $PTHREAD_LDFLAGS autotest.c >>config.log 2>&1 |
|||
if [ $? -eq 0 ] ; then |
|||
echo "found" |
|||
cleanup |
|||
echo "-> OK" >> config.log |
|||
return 0 |
|||
else |
|||
echo "not found" |
|||
echo "-> FAILED" >> config.log |
|||
echo "Failed program was:" >> config.log |
|||
cat autotest.c >> config.log |
|||
cleanup |
|||
return 1 |
|||
fi |
|||
} |
|||
|
|||
check_include() |
|||
{ |
|||
INCLUDE="$1" |
|||
echo -n "Testing for $INCLUDE: " |
|||
echo >> config.log |
|||
echo "Testing for $INCLUDE: " >> config.log |
|||
echo "#include <sys/types.h>" > autotest.c |
|||
echo "#include \"$INCLUDE\"" >> autotest.c |
|||
echo "#include <stdlib.h>" >> autotest.c |
|||
echo "int main (int argc, char **argv) { return 0; }" >> autotest.c |
|||
echo "$GCC $CFLAGS $PTHREAD_CFLAGS -o autotest $LDFLAGS $LIBS $PTHREAD_LDFLAGS autotest.c" >>config.log |
|||
$GCC $CFLAGS $PTHREAD_CFLAGS -o autotest $LDFLAGS $LIBS $PTHREAD_LDFLAGS autotest.c >>config.log 2>&1 |
|||
if [ $? -eq 0 ] ; then |
|||
echo "found" |
|||
echo "-> OK" >> config.log |
|||
_CFLAG=`echo "-DHAVE_$INCLUDE" | sed -e 's/[./]/_/g' | tr '[:lower:]' '[:upper:]'` |
|||
CFLAGS="$CFLAGS $_CFLAG" |
|||
cleanup |
|||
return 0 |
|||
else |
|||
echo "not found" |
|||
echo "-> FAILED" >> config.log |
|||
echo "Failed program was:" >> config.log |
|||
cat autotest.c >> config.log |
|||
cleanup |
|||
return 1 |
|||
fi |
|||
} |
|||
|
|||
check_macro() |
|||
{ |
|||
MACRO=$1 |
|||
while [ $# -ne 1 -a -n $1 ] ; do |
|||
shift |
|||
if [ "F$INCLUDE" = "F" ] ; then |
|||
INCLUDE="$1" |
|||
else |
|||
INCLUDE="$INCLUDE $1" |
|||
fi |
|||
done |
|||
echo -n "Testing for $MACRO: " |
|||
echo >> config.log |
|||
echo "Testing for $MACRO: " >> config.log |
|||
echo "#include <sys/types.h>" > autotest.c |
|||
for inc in $INCLUDE ; do |
|||
echo "#include \"$inc\"" >> autotest.c |
|||
done |
|||
echo "#include \"${INCLUDE}\"" >> autotest.c |
|||
echo "#include <stdlib.h>" >> autotest.c |
|||
echo "int main (int argc, char **argv) {" >>autotest.c |
|||
echo "#ifndef $MACRO" >>autotest.c |
|||
echo "#error \"$MACRO not defined\"" >>autotest.c |
|||
echo "#endif" >> autotest.c |
|||
echo "}" >>autotest.c |
|||
echo "$GCC $CFLAGS $PTHREAD_CFLAGS -o autotest $LDFLAGS $LIBS $PTHREAD_LDFLAGS autotest.c" >> config.log |
|||
$GCC $CFLAGS $PTHREAD_CFLAGS -o autotest $LDFLAGS $LIBS $PTHREAD_LDFLAGS autotest.c >>config.log 2>&1 |
|||
if [ $? -eq 0 ] ; then |
|||
echo "found" |
|||
cleanup |
|||
echo "-> OK" >> config.log |
|||
return 0 |
|||
else |
|||
echo "not found" |
|||
echo "-> FAILED" >> config.log |
|||
echo "Failed program was:" >> config.log |
|||
cat autotest.c >> config.log |
|||
cleanup |
|||
return 1 |
|||
fi |
|||
} |
|||
|
|||
|
|||
check_lib() |
|||
{ |
|||
LIB=$1 |
|||
while [ $# -ne 1 -a -n $1 ] ; do |
|||
shift |
|||
if [ "F$INCLUDE" = "F" ] ; then |
|||
INCLUDE="$1" |
|||
else |
|||
INCLUDE="$INCLUDE $1" |
|||
fi |
|||
done |
|||
echo -n "Testing for lib$LIB: " |
|||
echo >> config.log |
|||
echo "Testing for lib$LIB: " >> config.log |
|||
echo "#include <sys/types.h>" > autotest.c |
|||
if [ "F$INCLUDE" != "F" ] ; then |
|||
for inc in $INCLUDE ; do |
|||
echo "#include \"$inc\"" >> autotest.c |
|||
done |
|||
fi |
|||
echo "#include <stdlib.h>" >> autotest.c |
|||
echo "int main (int argc, char **argv) { return 0; }" >> autotest.c |
|||
echo "$GCC $CFLAGS $PTHREAD_CFLAGS -o autotest $LDFLAGS $LIBS -l$LIB $PTHREAD_LDFLAGS autotest.c" >>config.log |
|||
$GCC $CFLAGS $PTHREAD_CFLAGS -o autotest $LDFLAGS $LIBS -l$LIB $PTHREAD_LDFLAGS autotest.c >>config.log 2>&1 |
|||
if [ $? -eq 0 ] ; then |
|||
echo "found" |
|||
LIBS="$LIBS -l$LIB" |
|||
cleanup |
|||
echo "-> OK" >> config.log |
|||
return 0 |
|||
else |
|||
echo "not found" |
|||
echo "-> FAILED" >> config.log |
|||
echo "Failed program was:" >> config.log |
|||
cat autotest.c >> config.log |
|||
cleanup |
|||
return 1 |
|||
fi |
|||
} |
|||
|
|||
check_os() |
|||
{ |
|||
_OS=`uname -s` |
|||
case "$_OS" in |
|||
FreeBSD*) |
|||
OS="freebsd" |
|||
CFLAGS="${CFLAGS} -DFREEBSD" |
|||
INSTALL="/usr/bin/install -C -S -v" |
|||
MKDIR="/usr/bin/install -d -v" |
|||
MANPATH="${PREFIX}/man" ;; |
|||
Linux*) OS="linux" CFLAGS="${CFLAGS} -DLINUX -D_GNU_SOURCE" ;; |
|||
Solaris*) OS="solaris" CFLAGS="${CFLAGS} -DSOLARIS" ;; |
|||
*) OS="unknown" ;; |
|||
esac |
|||
} |
|||
|
|||
check_user() |
|||
{ |
|||
_user=$1 |
|||
echo -n "Checking for user $_user: " |
|||
grep $_user /etc/passwd > /dev/null 2>&1 |
|||
if [ $? -eq 0 ] ; then |
|||
echo "found" |
|||
return 0 |
|||
else |
|||
echo "not found" |
|||
return 1 |
|||
fi |
|||
} |
|||
|
|||
check_group() |
|||
{ |
|||
_group=$1 |
|||
echo -n "Checking for group $_group: " |
|||
grep $_group /etc/group > /dev/null 2>&1 |
|||
if [ $? -eq 0 ] ; then |
|||
echo "found" |
|||
return 0 |
|||
else |
|||
echo "not found" |
|||
return 1 |
|||
fi |
|||
} |
|||
|
|||
write_result() |
|||
{ |
|||
echo "Compiler: $GCC" >> config.log |
|||
echo "Make: $MAKE" >> config.log |
|||
echo "Sources: $SOURCES" >> config.log |
|||
echo "Cflags: $CFLAGS" >> config.log |
|||
echo "Ldflags: $LDFLAGS" >> config.log |
|||
echo "Libs: $LIBS" >> config.log |
|||
OBJECTS=`echo $SOURCES | sed -e 's/\.c/\.o/g'` |
|||
cat > $MAKEFILE << END |
|||
# This is ${EXEC} Makefile |
|||
# For options edit Makefile.in, this file is autogenerated by configure |
|||
|
|||
CC=$GCC |
|||
# Optimization flags |
|||
OPT_FLAGS=$OPT_FLAGS |
|||
# Compile time flags |
|||
CFLAGS=$CFLAGS |
|||
# Link time flags |
|||
LDFLAGS=$LDFLAGS |
|||
# Libraries to link |
|||
LIBS=$LIBS |
|||
# ${EXEC} sources |
|||
SOURCES=$SOURCES |
|||
# ${EXEC} objects |
|||
OBJECTS=$OBJECTS |
|||
# Version of product |
|||
VERION=$VERSION |
|||
# Detected operation system |
|||
OS=$OS |
|||
# Lex and yacc executables |
|||
LEX=$LEX |
|||
YACC=$YACC |
|||
# Pthread specific flags |
|||
PTHREAD_CFLAGS=$PTHREAD_CFLAGS |
|||
PTHREAD_LDFLAGS=$PTHREAD_LDFLAGS |
|||
# Prefix to install |
|||
PREFIX=$PREFIX |
|||
# Where local libs and includes are located |
|||
LOCALBASE=$LOCALBASE |
|||
# Install commands |
|||
INSTALL=$INSTALL |
|||
MKDIR=$MKDIR |
|||
# Executable name |
|||
EXEC=$EXEC |
|||
# User and group |
|||
RSPAMD_USER=$USER |
|||
RSPAMD_GROUP=$GROUP |
|||
# All target dependenses |
|||
TARGETS=$TARGETS |
|||
# Common dependenses |
|||
DEPS=$DEPS |
|||
# Path to install manual page |
|||
MANPATH=$MANPATH |
|||
|
|||
END |
|||
# Write build targets to makefile |
|||
|
|||
cat $MAKEFILE_IN >> $MAKEFILE |
|||
cat >> $MAKEFILE << END |
|||
${EXEC}: \$(OBJECTS) |
|||
\$(CC) \$(PTHREAD_LDFLAGS) \$(LDFLAGS) \$(OBJECTS) \$(LIBS) -o \$(EXEC) |
|||
END |
|||
for o in $OBJECTS ; do |
|||
SO=`echo $o | sed -e 's/\.o/\.c/g'` |
|||
cat >> $MAKEFILE << END |
|||
${o}: \$(DEPS) ${SO} |
|||
\$(CC) \$(OPT_FLAGS) \$(CFLAGS) \$(PTHREAD_CFLAGS) -c ${SO} |
|||
|
|||
END |
|||
done |
|||
cat >> $MAKEFILE << END |
|||
${LEX_OUTPUT}: cfg_file.h ${LEX_SRC} ${YACC_OUTPUT} |
|||
\$(LEX) -o${LEX_OUTPUT} ${LEX_SRC} |
|||
|
|||
${YACC_OUTPUT}: cfg_file.h ${YACC_SRC} |
|||
\$(YACC) -d -o ${YACC_OUTPUT} ${YACC_SRC} |
|||
END |
|||
|
|||
} |
|||
|
|||
|
|||
for option |
|||
do |
|||
case "$option" in |
|||
-*=*) value=`echo "$option" | sed -e 's/[-_a-zA-Z0-9]*=//'` ;; |
|||
*) value="" ;; |
|||
esac |
|||
|
|||
case "$option" in |
|||
--help) help=yes ;; |
|||
--prefix) PREFIX=$value ;; |
|||
--user) USER=$value ;; |
|||
--group) GROUP=$value ;; |
|||
--enable-debug) CFLAGS="$CFLAGS -DWITH_DEBUG" OPT_FLAGS="" ;; |
|||
--enable-opt) OPT_FLAGS="-O3 -pipe" ;; |
|||
*) |
|||
echo "$0: error: invalid option \"$option\"" |
|||
exit 1 |
|||
;; |
|||
esac |
|||
done |
|||
|
|||
if [ "F$help" = "Fyes" ] ; then |
|||
cat << END |
|||
|
|||
--help this message |
|||
|
|||
--prefix=PATH set the installation prefix |
|||
--enable-debug turn on extra debug messages |
|||
--enable-opt turn on extra optimization |
|||
--user=USER set user to use |
|||
--group=GROUP set group to use |
|||
END |
|||
exit 1 |
|||
fi |
|||
|
|||
CFLAGS="$CFLAGS -I$PREFIX/include" |
|||
LDFLAGS="$LDFLAGS -L$PREFIX/lib" |
|||
|
|||
echo "Starting configure for rmilter" >config.log |
|||
echo $0 $@ >> config.log |
|||
|
|||
check_compiler |
|||
check_make |
|||
check_lex |
|||
check_yacc |
|||
check_os |
|||
|
|||
check_lib "event" "event.h" |
|||
if [ $? -eq 1 ] ; then |
|||
echo "Libevent not found, check config.log for details" |
|||
exit 1 |
|||
fi |
|||
|
|||
check_lib "m" |
|||
check_lib "pcre" |
|||
check_lib "md" |
|||
if [ $? -eq 1 ] ; then |
|||
cp $COMPAT_DIR/md5.c . |
|||
cp $COMPAT_DIR/md5.h . |
|||
SOURCES="$SOURCES md5.c" |
|||
CFLAGS="$CFLAGS -DHAVE_OWN_MD5" |
|||
DEPS="$DEPS md5.h" |
|||
fi |
|||
|
|||
check_lib "util" |
|||
if [ $? -eq 0 ] ; then |
|||
CFLAGS="$CFLAGS -DHAVE_LIBUTIL" |
|||
fi |
|||
|
|||
check_function "pidfile_open" "sys/param.h" "libutil.h" |
|||
if [ $? -eq 0 ] ; then |
|||
CFLAGS="$CFLAGS -DHAVE_PIDFILE" |
|||
fi |
|||
|
|||
check_function "strlcpy" "string.h" |
|||
if [ $? -eq 1 ] ; then |
|||
cp $COMPAT_DIR/strlcpy.c . |
|||
cp $COMPAT_DIR/strlcpy.h . |
|||
SOURCES="$SOURCES strlcpy.c" |
|||
CFLAGS="$CFLAGS -DHAVE_STRLCPY_H" |
|||
DEPS="$DEPS strlcpy.h" |
|||
fi |
|||
check_function "bzero" "string.h" |
|||
check_function "srandomdev" |
|||
if [ $? -eq 0 ] ; then |
|||
CFLAGS="$CFLAGS -DHAVE_SRANDOMDEV" |
|||
fi |
|||
|
|||
check_function "setproctitle" "unistd.h" |
|||
if [ $? -eq 0 ] ; then |
|||
CFLAGS="$CFLAGS -DHAVE_SETPROCTITLE" |
|||
fi |
|||
|
|||
check_include "endian.h" |
|||
check_include "libutil.h" |
|||
check_include "machine/endian.h" |
|||
check_include "sys/time.h" |
|||
check_include "time.h" |
|||
check_include "stdint.h" |
|||
if [ $? -eq 1 ] ; then |
|||
check_include "inttypes.h" |
|||
fi |
|||
check_include "strlcpy.h" |
|||
check_include "md5.h" |
|||
check_include "sys/queue.h" |
|||
if [ $? -eq 1 ] ; then |
|||
cp $COMPAT_DIR/queue.h . |
|||
DEPS="$DEPS queue.h" |
|||
fi |
|||
check_macro "SLIST_FOREACH_SAFE" "sys/queue.h" |
|||
if [ $? -eq 1 ] ; then |
|||
cp $COMPAT_DIR/queue.h . |
|||
CFLAGS="$CFLAGS -DOWN_QUEUE_H" |
|||
DEPS="$DEPS queue.h" |
|||
fi |
|||
|
|||
check_macro "PATH_MAX" "limits.h" |
|||
if [ $? -eq 1 ] ; then |
|||
check_macro "MAXPATHLEN" "sys/param.h" |
|||
if [ $? -eq 1 ] ; then |
|||
CFLAGS="$CFLAGS -DHAVE_MAXPATHLEN -DMAXPATHLEN=4096" |
|||
else |
|||
CFLAGS="$CFLAGS -DHAVE_MAXPATHLEN" |
|||
fi |
|||
else |
|||
CFLAGS="$CFLAGS -DHAVE_PATH_MAX" |
|||
fi |
|||
|
|||
check_group $GROUP |
|||
if [ $? -ne 0 ] ; then |
|||
TARGETS="$TARGETS creategroup" |
|||
fi |
|||
check_user $USER |
|||
if [ $? -ne 0 ] ; then |
|||
TARGETS="$TARGETS createuser" |
|||
fi |
|||
write_result |
|||
@ -0,0 +1,324 @@ |
|||
|
|||
#include <sys/types.h> |
|||
#include <sys/time.h> |
|||
#include <sys/wait.h> |
|||
#include <sys/param.h> |
|||
|
|||
#include <unistd.h> |
|||
#include <stdio.h> |
|||
#include <stdlib.h> |
|||
#include <string.h> |
|||
#include <time.h> |
|||
#include <errno.h> |
|||
#include <signal.h> |
|||
#ifdef HAVE_LIBUTIL_H |
|||
#include <libutil.h> |
|||
#endif |
|||
#include <syslog.h> |
|||
|
|||
#include "main.h" |
|||
#include "cfg_file.h" |
|||
#include "util.h" |
|||
|
|||
struct config_file *cfg; |
|||
|
|||
static void sig_handler (int ); |
|||
static struct rspamd_worker * fork_worker (struct rspamd_main *, int, int, enum process_type); |
|||
|
|||
sig_atomic_t do_restart; |
|||
sig_atomic_t do_terminate; |
|||
sig_atomic_t child_dead; |
|||
sig_atomic_t child_ready; |
|||
|
|||
extern int yynerrs; |
|||
extern FILE *yyin; |
|||
|
|||
static |
|||
void sig_handler (int signo) |
|||
{ |
|||
switch (signo) { |
|||
case SIGHUP: |
|||
do_restart = 1; |
|||
break; |
|||
case SIGINT: |
|||
case SIGTERM: |
|||
do_terminate = 1; |
|||
break; |
|||
case SIGCHLD: |
|||
child_dead = 1; |
|||
break; |
|||
case SIGUSR2: |
|||
child_ready = 1; |
|||
break; |
|||
} |
|||
} |
|||
|
|||
static struct rspamd_worker * |
|||
fork_worker (struct rspamd_main *rspamd, int listen_sock, int reconfig, enum process_type type) |
|||
{ |
|||
struct rspamd_worker *cur; |
|||
char *cfg_file; |
|||
FILE *f; |
|||
struct config_file *tmp_cfg; |
|||
/* Starting worker process */ |
|||
cur = (struct rspamd_worker *)malloc (sizeof (struct rspamd_worker)); |
|||
if (cur) { |
|||
/* Reconfig needed */ |
|||
if (reconfig) { |
|||
tmp_cfg = (struct config_file *) malloc (sizeof (struct config_file)); |
|||
if (tmp_cfg) { |
|||
cfg_file = strdup (rspamd->cfg->cfg_name); |
|||
bzero (tmp_cfg, sizeof (struct config_file)); |
|||
f = fopen (rspamd->cfg->cfg_name , "r"); |
|||
if (f == NULL) { |
|||
msg_warn ("fork_worker: cannot open file: %s", rspamd->cfg->cfg_name ); |
|||
} |
|||
else { |
|||
yyin = f; |
|||
yyrestart (yyin); |
|||
|
|||
if (yyparse() != 0 || yynerrs > 0) { |
|||
msg_warn ("fork_worker: yyparse: cannot parse config file, %d errors", yynerrs); |
|||
fclose (f); |
|||
} |
|||
else { |
|||
free_config (rspamd->cfg); |
|||
free (rspamd->cfg); |
|||
rspamd->cfg = tmp_cfg; |
|||
rspamd->cfg->cfg_name = cfg_file; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
bzero (cur, sizeof (struct rspamd_worker)); |
|||
TAILQ_INSERT_HEAD (&rspamd->workers, cur, next); |
|||
cur->srv = rspamd; |
|||
cur->pid = fork(); |
|||
switch (cur->pid) { |
|||
case 0: |
|||
/* TODO: add worker code */ |
|||
switch (type) { |
|||
case TYPE_WORKER: |
|||
default: |
|||
setproctitle ("worker process"); |
|||
pidfile_close (rspamd->pfh); |
|||
msg_info ("fork_worker: starting worker process %d", getpid ()); |
|||
cur->type = TYPE_WORKER; |
|||
start_worker (cur, listen_sock); |
|||
break; |
|||
} |
|||
break; |
|||
case -1: |
|||
msg_err ("fork_worker: cannot fork main process. %m"); |
|||
pidfile_remove (rspamd->pfh); |
|||
exit (-errno); |
|||
break; |
|||
} |
|||
} |
|||
|
|||
return cur; |
|||
} |
|||
|
|||
int |
|||
main (int argc, char **argv) |
|||
{ |
|||
struct rspamd_main *rspamd; |
|||
int res = 0, i, listen_sock; |
|||
struct sigaction signals; |
|||
struct rspamd_worker *cur, *cur_tmp, *active_worker; |
|||
struct sockaddr_un *un_addr; |
|||
FILE *f; |
|||
|
|||
rspamd = (struct rspamd_main *)malloc (sizeof (struct rspamd_main)); |
|||
bzero (rspamd, sizeof (struct rspamd_main)); |
|||
cfg = (struct config_file *)malloc (sizeof (struct config_file)); |
|||
rspamd->cfg = cfg; |
|||
if (!rspamd || !rspamd->cfg) { |
|||
fprintf(stderr, "Cannot allocate memory\n"); |
|||
exit(-errno); |
|||
} |
|||
|
|||
do_terminate = 0; |
|||
do_restart = 0; |
|||
child_dead = 0; |
|||
child_ready = 0; |
|||
active_worker = NULL; |
|||
|
|||
bzero (rspamd->cfg, sizeof (struct config_file)); |
|||
init_defaults (rspamd->cfg); |
|||
|
|||
bzero (&signals, sizeof (struct sigaction)); |
|||
|
|||
rspamd->cfg->cfg_name = strdup (FIXED_CONFIG_FILE); |
|||
read_cmd_line (argc, argv, rspamd->cfg); |
|||
|
|||
openlog("rspamd", LOG_PID, LOG_MAIL); |
|||
msg_warn ("(main) starting..."); |
|||
|
|||
#ifndef HAVE_SETPROCTITLE |
|||
init_title (argc, argv, environ); |
|||
#endif |
|||
|
|||
f = fopen (rspamd->cfg->cfg_name , "r"); |
|||
if (f == NULL) { |
|||
msg_warn ("cannot open file: %s", rspamd->cfg->cfg_name ); |
|||
return EBADF; |
|||
} |
|||
yyin = f; |
|||
|
|||
if (yyparse() != 0 || yynerrs > 0) { |
|||
msg_warn ("yyparse: cannot parse config file, %d errors", yynerrs); |
|||
return EBADF; |
|||
} |
|||
|
|||
fclose (f); |
|||
rspamd->cfg->cfg_name = strdup (rspamd->cfg->cfg_name ); |
|||
|
|||
/* Strictly set temp dir */ |
|||
if (!rspamd->cfg->temp_dir) { |
|||
msg_warn ("tempdir is not set, trying to use $TMPDIR"); |
|||
rspamd->cfg->temp_dir = getenv ("TMPDIR"); |
|||
|
|||
if (!rspamd->cfg->temp_dir) { |
|||
rspamd->cfg->temp_dir = strdup ("/tmp"); |
|||
} |
|||
} |
|||
|
|||
if (!rspamd->cfg->no_fork && daemon (1, 1) == -1) { |
|||
fprintf (stderr, "Cannot daemonize\n"); |
|||
exit (-errno); |
|||
} |
|||
|
|||
if (write_pid (rspamd) == -1) { |
|||
msg_err ("main: cannot write pid file %s", rspamd->cfg->pid_file); |
|||
exit (-errno); |
|||
} |
|||
|
|||
rspamd->pid = getpid(); |
|||
rspamd->type = TYPE_MAIN; |
|||
|
|||
init_signals (&signals, sig_handler); |
|||
/* Block signals to use sigsuspend in future */ |
|||
sigprocmask(SIG_BLOCK, &signals.sa_mask, NULL); |
|||
|
|||
if (rspamd->cfg->bind_family == AF_INET) { |
|||
if ((listen_sock = make_socket (rspamd->cfg->bind_host, rspamd->cfg->bind_port)) == -1) { |
|||
msg_err ("main: cannot create tcp listen socket. %m"); |
|||
exit(-errno); |
|||
} |
|||
} |
|||
else { |
|||
un_addr = (struct sockaddr_un *) malloc (sizeof (struct sockaddr_un)); |
|||
if (!un_addr || (listen_sock = make_unix_socket (rspamd->cfg->bind_host, un_addr)) == -1) { |
|||
msg_err ("main: cannot create unix listen socket. %m"); |
|||
exit(-errno); |
|||
} |
|||
} |
|||
|
|||
if (listen (listen_sock, -1) == -1) { |
|||
msg_err ("main: cannot listen on socket. %m"); |
|||
exit(-errno); |
|||
} |
|||
|
|||
TAILQ_INIT (&rspamd->workers); |
|||
|
|||
setproctitle ("main process"); |
|||
|
|||
for (i = 0; i < cfg->workers_number; i++) { |
|||
fork_worker (rspamd, listen_sock, 0, TYPE_WORKER); |
|||
} |
|||
|
|||
|
|||
/* Signal processing cycle */ |
|||
for (;;) { |
|||
msg_debug ("main: calling sigsuspend"); |
|||
sigemptyset (&signals.sa_mask); |
|||
sigsuspend (&signals.sa_mask); |
|||
if (do_terminate) { |
|||
msg_debug ("main: catch termination signal, waiting for childs"); |
|||
pass_signal_worker (&rspamd->workers, SIGTERM); |
|||
break; |
|||
} |
|||
if (child_dead) { |
|||
child_dead = 0; |
|||
msg_debug ("main: catch SIGCHLD signal, finding terminated worker"); |
|||
/* Remove dead child form childs list */ |
|||
pid_t wrk = waitpid (0, &res, 0); |
|||
TAILQ_FOREACH_SAFE (cur, &rspamd->workers, next, cur_tmp) { |
|||
if (wrk == cur->pid) { |
|||
/* Catch situations if active worker is abnormally terminated */ |
|||
if (cur == active_worker) { |
|||
active_worker = NULL; |
|||
} |
|||
TAILQ_REMOVE(&rspamd->workers, cur, next); |
|||
if (WIFEXITED (res) && WEXITSTATUS (res) == 0) { |
|||
/* Normal worker termination, do not fork one more */ |
|||
msg_info ("main: worker process %d terminated normally", cur->pid); |
|||
} |
|||
else { |
|||
if (WIFSIGNALED (res)) { |
|||
msg_warn ("main: worker process %d terminated abnormally by signal: %d", |
|||
cur->pid, WTERMSIG(res)); |
|||
} |
|||
else { |
|||
msg_warn ("main: worker process %d terminated abnormally", cur->pid); |
|||
} |
|||
/* Fork another worker in replace of dead one */ |
|||
fork_worker (rspamd, listen_sock, 0, cur->type); |
|||
} |
|||
free (cur); |
|||
} |
|||
} |
|||
} |
|||
if (do_restart) { |
|||
do_restart = 0; |
|||
|
|||
if (active_worker == NULL) { |
|||
/* Start new worker that would reread configuration*/ |
|||
active_worker = fork_worker (rspamd, listen_sock, 1, TYPE_WORKER); |
|||
} |
|||
/* Do not start new workers untill active worker is not ready for accept */ |
|||
} |
|||
if (child_ready) { |
|||
child_ready = 0; |
|||
|
|||
if (active_worker != NULL) { |
|||
msg_info ("main: worker process %d has been successfully started", active_worker->pid); |
|||
TAILQ_FOREACH_SAFE (cur, &rspamd->workers, next, cur_tmp) { |
|||
if (cur != active_worker && !cur->is_dying) { |
|||
/* Send to old workers SIGUSR2 */ |
|||
kill (cur->pid, SIGUSR2); |
|||
cur->is_dying = 1; |
|||
} |
|||
} |
|||
active_worker = NULL; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/* Wait for workers termination */ |
|||
while (!TAILQ_EMPTY(&rspamd->workers)) { |
|||
cur = TAILQ_FIRST(&rspamd->workers); |
|||
waitpid (cur->pid, &res, 0); |
|||
msg_debug ("main(cleaning): worker process %d terminated", cur->pid); |
|||
TAILQ_REMOVE(&rspamd->workers, cur, next); |
|||
free(cur); |
|||
} |
|||
|
|||
msg_info ("main: terminating..."); |
|||
|
|||
|
|||
if (rspamd->cfg->bind_family == AF_UNIX) { |
|||
unlink (rspamd->cfg->bind_host); |
|||
} |
|||
|
|||
free_config (rspamd->cfg); |
|||
free (rspamd->cfg); |
|||
free (rspamd); |
|||
|
|||
return (res); |
|||
} |
|||
|
|||
/* |
|||
* vi:ts=4 |
|||
*/ |
|||
@ -0,0 +1,71 @@ |
|||
#ifndef RPOP_MAIN_H |
|||
#define RPOP_MAIN_H |
|||
|
|||
#include <sys/types.h> |
|||
#include <sys/socket.h> |
|||
#ifndef OWN_QUEUE_H |
|||
#include <sys/queue.h> |
|||
#else |
|||
#include "queue.h" |
|||
#endif |
|||
#include <sys/time.h> |
|||
|
|||
#include <sys/un.h> |
|||
#include <netinet/in.h> |
|||
#include <arpa/inet.h> |
|||
|
|||
#include <signal.h> |
|||
|
|||
/* Default values */ |
|||
#define FIXED_CONFIG_FILE "./rspamd.conf" |
|||
/* Time in seconds to exit for old worker */ |
|||
#define SOFT_SHUTDOWN_TIME 60 |
|||
|
|||
/* Logging in postfix style */ |
|||
#define msg_err(args...) syslog(LOG_ERR, ##args) |
|||
#define msg_warn(args...) syslog(LOG_WARNING, ##args) |
|||
#define msg_info(args...) syslog(LOG_INFO, ##args) |
|||
#define msg_debug(args...) syslog(LOG_DEBUG, ##args) |
|||
|
|||
/* Process type: main or worker */ |
|||
enum process_type { |
|||
TYPE_MAIN, |
|||
TYPE_WORKER, |
|||
}; |
|||
|
|||
/* Worker process structure */ |
|||
struct rspamd_worker { |
|||
pid_t pid; |
|||
char is_initialized; |
|||
char is_dying; |
|||
TAILQ_ENTRY (rspamd_worker) next; |
|||
struct rspamd_main *srv; |
|||
enum process_type type; |
|||
}; |
|||
|
|||
struct pidfh; |
|||
struct config_file; |
|||
|
|||
/* Struct that determine main server object (for logging purposes) */ |
|||
struct rspamd_main { |
|||
struct config_file *cfg; |
|||
pid_t pid; |
|||
/* Pid file structure */ |
|||
struct pidfh *pfh; |
|||
enum process_type type; |
|||
unsigned ev_initialized:1; |
|||
|
|||
TAILQ_HEAD (workq, rspamd_worker) workers; |
|||
}; |
|||
|
|||
struct worker_task { |
|||
int id; |
|||
}; |
|||
|
|||
void start_worker (struct rspamd_worker *worker, int listen_sock); |
|||
|
|||
#endif |
|||
|
|||
/* |
|||
* vi:ts=4 |
|||
*/ |
|||
@ -0,0 +1,743 @@ |
|||
#ifdef _THREAD_SAFE |
|||
#include <pthread.h> |
|||
#endif |
|||
|
|||
#include <stdarg.h> |
|||
|
|||
#include <sys/types.h> |
|||
#include <sys/stat.h> |
|||
#include <sys/param.h> |
|||
#include <stdio.h> |
|||
#include <stdlib.h> |
|||
#include <string.h> |
|||
#include <sysexits.h> |
|||
#include <unistd.h> |
|||
#include <syslog.h> |
|||
|
|||
#include <netinet/in.h> |
|||
#include <arpa/inet.h> |
|||
#include <sys/socket.h> |
|||
#include <sys/poll.h> |
|||
#include <errno.h> |
|||
#include <fcntl.h> |
|||
#include <sys/uio.h> |
|||
|
|||
#include "memcached.h" |
|||
|
|||
#define CRLF "\r\n" |
|||
#define END_TRAILER "END" CRLF |
|||
#define STORED_TRAILER "STORED" CRLF |
|||
#define NOT_STORED_TRAILER "NOT STORED" CRLF |
|||
#define EXISTS_TRAILER "EXISTS" CRLF |
|||
#define DELETED_TRAILER "DELETED" CRLF |
|||
#define NOT_FOUND_TRAILER "NOT_FOUND" CRLF |
|||
#define CLIENT_ERROR_TRAILER "CLIENT_ERROR" |
|||
#define SERVER_ERROR_TRAILER "SERVER_ERROR" |
|||
|
|||
#define READ_BUFSIZ 1500 |
|||
#define MAX_RETRIES 3 |
|||
|
|||
/* Header for udp protocol */ |
|||
struct memc_udp_header |
|||
{ |
|||
uint16_t req_id; |
|||
uint16_t seq_num; |
|||
uint16_t dg_sent; |
|||
uint16_t unused; |
|||
}; |
|||
|
|||
/* |
|||
* Poll file descriptor for read or write during specified timeout |
|||
*/ |
|||
static int |
|||
poll_d (int fd, u_char want_read, u_char want_write, int timeout) |
|||
{ |
|||
int r; |
|||
struct pollfd fds[1]; |
|||
|
|||
fds->fd = fd; |
|||
fds->revents = 0; |
|||
fds->events = 0; |
|||
|
|||
if (want_read != 0) { |
|||
fds->events |= POLLIN; |
|||
} |
|||
if (want_write != 0) { |
|||
fds->events |= POLLOUT; |
|||
} |
|||
while ((r = poll(fds, 1, timeout)) < 0) { |
|||
if (errno != EINTR) |
|||
break; |
|||
} |
|||
|
|||
return r; |
|||
} |
|||
|
|||
/* |
|||
* Write to syslog if OPT_DEBUG is specified |
|||
*/ |
|||
static void |
|||
memc_log (const memcached_ctx_t *ctx, int line, const char *fmt, ...) |
|||
{ |
|||
va_list args; |
|||
if (ctx->options & MEMC_OPT_DEBUG) { |
|||
va_start (args, fmt); |
|||
syslog (LOG_DEBUG, "memc_debug(%d): host: %s, port: %d", line, inet_ntoa (ctx->addr), ntohs (ctx->port)); |
|||
vsyslog (LOG_DEBUG, fmt, args); |
|||
va_end (args); |
|||
} |
|||
} |
|||
|
|||
/* |
|||
* Make socket for udp connection |
|||
*/ |
|||
static int |
|||
memc_make_udp_sock (memcached_ctx_t *ctx) |
|||
{ |
|||
struct sockaddr_in sc; |
|||
int ofl; |
|||
|
|||
bzero (&sc, sizeof (struct sockaddr_in *)); |
|||
sc.sin_family = AF_INET; |
|||
sc.sin_port = ctx->port; |
|||
memcpy (&sc.sin_addr, &ctx->addr, sizeof (struct in_addr)); |
|||
|
|||
ctx->sock = socket (PF_INET, SOCK_DGRAM, 0); |
|||
|
|||
if (ctx->sock == -1) { |
|||
memc_log (ctx, __LINE__, "memc_make_udp_sock: socket() failed: %m"); |
|||
return -1; |
|||
} |
|||
|
|||
/* set nonblocking */ |
|||
ofl = fcntl(ctx->sock, F_GETFL, 0); |
|||
fcntl(ctx->sock, F_SETFL, ofl | O_NONBLOCK); |
|||
|
|||
/* |
|||
* Call connect to set default destination for datagrams |
|||
* May not block |
|||
*/ |
|||
return connect (ctx->sock, (struct sockaddr*)&sc, sizeof (struct sockaddr_in)); |
|||
} |
|||
|
|||
/* |
|||
* Make socket for tcp connection |
|||
*/ |
|||
static int |
|||
memc_make_tcp_sock (memcached_ctx_t *ctx) |
|||
{ |
|||
struct sockaddr_in sc; |
|||
int ofl, r; |
|||
|
|||
bzero (&sc, sizeof (struct sockaddr_in *)); |
|||
sc.sin_family = AF_INET; |
|||
sc.sin_port = ctx->port; |
|||
memcpy (&sc.sin_addr, &ctx->addr, sizeof (struct in_addr)); |
|||
|
|||
ctx->sock = socket (PF_INET, SOCK_STREAM, 0); |
|||
|
|||
if (ctx->sock == -1) { |
|||
memc_log (ctx, __LINE__, "memc_make_tcp_sock: socket() failed: %m"); |
|||
return -1; |
|||
} |
|||
|
|||
/* set nonblocking */ |
|||
ofl = fcntl(ctx->sock, F_GETFL, 0); |
|||
fcntl(ctx->sock, F_SETFL, ofl | O_NONBLOCK); |
|||
|
|||
if ((r = connect (ctx->sock, (struct sockaddr*)&sc, sizeof (struct sockaddr_in))) == -1) { |
|||
if (errno != EINPROGRESS) { |
|||
close (ctx->sock); |
|||
ctx->sock = -1; |
|||
memc_log (ctx, __LINE__, "memc_make_tcp_sock: connect() failed: %m"); |
|||
return -1; |
|||
} |
|||
} |
|||
/* Get write readiness */ |
|||
if (poll_d (ctx->sock, 0, 1, ctx->timeout) == 1) { |
|||
return 0; |
|||
} |
|||
else { |
|||
memc_log (ctx, __LINE__, "memc_make_tcp_sock: poll() timeout"); |
|||
close (ctx->sock); |
|||
ctx->sock = -1; |
|||
return -1; |
|||
} |
|||
} |
|||
|
|||
/* |
|||
* Parse VALUE reply from server and set len argument to value returned by memcached |
|||
*/ |
|||
static int |
|||
memc_parse_header (char *buf, size_t *len, char **end) |
|||
{ |
|||
char *p, *c; |
|||
int i; |
|||
|
|||
/* VALUE <key> <flags> <bytes> [<cas unique>]\r\n */ |
|||
c = strstr (buf, CRLF); |
|||
if (c == NULL) { |
|||
return -1; |
|||
} |
|||
*end = c + sizeof (CRLF) - 1; |
|||
|
|||
if (strncmp (buf, "VALUE ", sizeof ("VALUE ") - 1) == 0) { |
|||
p = buf + sizeof ("VALUE ") - 1; |
|||
|
|||
/* Read bytes value and ignore all other fields, such as flags and key */ |
|||
for (i = 0; i < 2; i++) { |
|||
while (p++ < c && *p != ' '); |
|||
|
|||
if (p > c) { |
|||
return -1; |
|||
} |
|||
} |
|||
*len = strtoul (p, &c, 10); |
|||
return 1; |
|||
} |
|||
/* If value not found memcached return just END\r\n , in this case return 0 */ |
|||
else if (strncmp (buf, END_TRAILER, sizeof (END_TRAILER) - 1) == 0) { |
|||
return 0; |
|||
} |
|||
|
|||
return -1; |
|||
} |
|||
/* |
|||
* Common read command handler for memcached |
|||
*/ |
|||
memc_error_t |
|||
memc_read (memcached_ctx_t *ctx, const char *cmd, memcached_param_t *params, size_t *nelem) |
|||
{ |
|||
char read_buf[READ_BUFSIZ]; |
|||
char *p; |
|||
int i, retries; |
|||
ssize_t r, sum = 0, written = 0; |
|||
size_t datalen; |
|||
struct memc_udp_header header; |
|||
struct iovec iov[2]; |
|||
|
|||
for (i = 0; i < *nelem; i++) { |
|||
if (ctx->protocol == UDP_TEXT) { |
|||
/* Send udp header */ |
|||
bzero (&header, sizeof (header)); |
|||
header.dg_sent = htons (1); |
|||
header.req_id = ctx->count; |
|||
} |
|||
|
|||
r = snprintf (read_buf, READ_BUFSIZ, "%s %s" CRLF, cmd, params[i].key); |
|||
memc_log (ctx, __LINE__, "memc_read: send read request to memcached: %s", read_buf); |
|||
if (ctx->protocol == UDP_TEXT) { |
|||
iov[0].iov_base = &header; |
|||
iov[0].iov_len = sizeof (struct memc_udp_header); |
|||
iov[1].iov_base = read_buf; |
|||
iov[1].iov_len = r; |
|||
writev (ctx->sock, iov, 2); |
|||
} |
|||
else { |
|||
write (ctx->sock, read_buf, r); |
|||
} |
|||
|
|||
/* Read reply from server */ |
|||
retries = 0; |
|||
while (ctx->protocol == UDP_TEXT) { |
|||
if (poll_d (ctx->sock, 1, 0, ctx->timeout) != 1) { |
|||
return SERVER_TIMEOUT; |
|||
} |
|||
iov[0].iov_base = &header; |
|||
iov[0].iov_len = sizeof (struct memc_udp_header); |
|||
iov[1].iov_base = read_buf; |
|||
iov[1].iov_len = READ_BUFSIZ; |
|||
if ((r = readv (ctx->sock, iov, 2)) == -1) { |
|||
return SERVER_ERROR; |
|||
} |
|||
memc_log (ctx, __LINE__, "memc_read: got read_buf: %s", read_buf); |
|||
if (header.req_id != ctx->count && retries < MAX_RETRIES) { |
|||
memc_log (ctx, __LINE__, "memc_read: got wrong packet id: %d, %d was awaited", header.req_id, ctx->count); |
|||
retries++; |
|||
/* Not our reply packet */ |
|||
continue; |
|||
} |
|||
break; |
|||
} |
|||
if (ctx->protocol != UDP_TEXT) { |
|||
if (poll_d (ctx->sock, 1, 0, ctx->timeout) != 1) { |
|||
memc_log (ctx, __LINE__, "memc_read: timeout waiting reply"); |
|||
return SERVER_TIMEOUT; |
|||
} |
|||
r = read (ctx->sock, read_buf, READ_BUFSIZ - 1); |
|||
} |
|||
|
|||
if (r > 0) { |
|||
sum += r; |
|||
read_buf[r] = 0; |
|||
r = memc_parse_header (read_buf, &datalen, &p); |
|||
if (r < 0) { |
|||
memc_log (ctx, __LINE__, "memc_read: cannot parse memcached reply"); |
|||
return SERVER_ERROR; |
|||
} |
|||
else if (r == 0) { |
|||
memc_log (ctx, __LINE__, "memc_read: record does not exists"); |
|||
return NOT_EXISTS; |
|||
} |
|||
|
|||
if (datalen != params[i].bufsize) { |
|||
memc_log (ctx, __LINE__, "memc_read: user's buffer is too small: %zd, %zd required", params[i].bufsize, datalen); |
|||
return WRONG_LENGTH; |
|||
} |
|||
|
|||
/* Subtract from sum parsed header's length */ |
|||
sum -= p - read_buf; |
|||
/* Check if we already have all data in buffer */ |
|||
if (sum >= datalen + sizeof (END_TRAILER) + sizeof (CRLF) - 2) { |
|||
/* Store all data in param's buffer */ |
|||
memcpy (params[i].buf, p, datalen); |
|||
/* Increment count */ |
|||
ctx->count++; |
|||
return OK; |
|||
} |
|||
else { |
|||
/* Store this part of data in param's buffer */ |
|||
memcpy (params[i].buf, p, sum); |
|||
written += sum; |
|||
} |
|||
} |
|||
else { |
|||
memc_log (ctx, __LINE__, "memc_read: read(v) failed: %d, %m", r); |
|||
return SERVER_ERROR; |
|||
} |
|||
/* Read data from multiply datagrams */ |
|||
p = read_buf; |
|||
|
|||
while (sum < datalen + sizeof (END_TRAILER) + sizeof (CRLF) - 2) { |
|||
retries = 0; |
|||
while (ctx->protocol == UDP_TEXT) { |
|||
if (poll_d (ctx->sock, 1, 0, ctx->timeout) != 1) { |
|||
memc_log (ctx, __LINE__, "memc_read: timeout waiting reply"); |
|||
return SERVER_TIMEOUT; |
|||
} |
|||
iov[0].iov_base = &header; |
|||
iov[0].iov_len = sizeof (struct memc_udp_header); |
|||
iov[1].iov_base = read_buf; |
|||
iov[1].iov_len = READ_BUFSIZ; |
|||
if ((r = readv (ctx->sock, iov, 2)) == -1) { |
|||
memc_log (ctx, __LINE__, "memc_read: read(v) failed: %d, %m", r); |
|||
return SERVER_ERROR; |
|||
} |
|||
if (header.req_id != ctx->count && retries < MAX_RETRIES) { |
|||
memc_log (ctx, __LINE__, "memc_read: got wrong packet id: %d, %d was awaited", header.req_id, ctx->count); |
|||
retries ++; |
|||
/* Not our reply packet */ |
|||
continue; |
|||
} |
|||
} |
|||
if (ctx->protocol != UDP_TEXT) { |
|||
if (poll_d (ctx->sock, 1, 0, ctx->timeout) != 1) { |
|||
memc_log (ctx, __LINE__, "memc_read: timeout waiting reply"); |
|||
return SERVER_TIMEOUT; |
|||
} |
|||
r = read (ctx->sock, read_buf, READ_BUFSIZ - 1); |
|||
} |
|||
|
|||
p = read_buf; |
|||
sum += r; |
|||
if (r <= 0) { |
|||
break; |
|||
} |
|||
/* Copy received buffer to result buffer */ |
|||
while (r--) { |
|||
/* Break on reading END\r\n */ |
|||
if (strncmp (p, END_TRAILER, sizeof (END_TRAILER) - 1) == 0) { |
|||
break; |
|||
} |
|||
if (written < datalen) { |
|||
params[i].buf[written++] = *p++; |
|||
} |
|||
} |
|||
} |
|||
/* Increment count */ |
|||
ctx->count++; |
|||
} |
|||
|
|||
return OK; |
|||
} |
|||
|
|||
/* |
|||
* Common write command handler for memcached |
|||
*/ |
|||
memc_error_t |
|||
memc_write (memcached_ctx_t *ctx, const char *cmd, memcached_param_t *params, size_t *nelem, int expire) |
|||
{ |
|||
char read_buf[READ_BUFSIZ]; |
|||
int i, retries, ofl; |
|||
ssize_t r; |
|||
struct memc_udp_header header; |
|||
struct iovec iov[4]; |
|||
|
|||
for (i = 0; i < *nelem; i++) { |
|||
if (ctx->protocol == UDP_TEXT) { |
|||
/* Send udp header */ |
|||
bzero (&header, sizeof (header)); |
|||
header.dg_sent = htons (1); |
|||
header.req_id = ctx->count; |
|||
} |
|||
|
|||
r = snprintf (read_buf, READ_BUFSIZ, "%s %s 0 %d %zu" CRLF, cmd, params[i].key, expire, params[i].bufsize); |
|||
memc_log (ctx, __LINE__, "memc_write: send write request to memcached: %s", read_buf); |
|||
/* Set socket blocking */ |
|||
ofl = fcntl(ctx->sock, F_GETFL, 0); |
|||
fcntl(ctx->sock, F_SETFL, ofl & (~O_NONBLOCK)); |
|||
|
|||
if (ctx->protocol == UDP_TEXT) { |
|||
iov[0].iov_base = &header; |
|||
iov[0].iov_len = sizeof (struct memc_udp_header); |
|||
iov[1].iov_base = read_buf; |
|||
iov[1].iov_len = r; |
|||
iov[2].iov_base = params[i].buf; |
|||
iov[2].iov_len = params[i].bufsize; |
|||
iov[3].iov_base = CRLF; |
|||
iov[3].iov_len = sizeof (CRLF) - 1; |
|||
writev (ctx->sock, iov, 4); |
|||
} |
|||
else { |
|||
iov[0].iov_base = read_buf; |
|||
iov[0].iov_len = r; |
|||
iov[1].iov_base = params[i].buf; |
|||
iov[1].iov_len = params[i].bufsize; |
|||
iov[2].iov_base = CRLF; |
|||
iov[2].iov_len = sizeof (CRLF) - 1; |
|||
writev (ctx->sock, iov, 3); |
|||
} |
|||
|
|||
/* Restore socket mode */ |
|||
fcntl(ctx->sock, F_SETFL, ofl); |
|||
/* Read reply from server */ |
|||
if (poll_d (ctx->sock, 1, 0, ctx->timeout) != 1) { |
|||
memc_log (ctx, __LINE__, "memc_write: server timeout while reading reply"); |
|||
return SERVER_ERROR; |
|||
} |
|||
/* Read header */ |
|||
retries = 0; |
|||
while (ctx->protocol == UDP_TEXT) { |
|||
if (poll_d (ctx->sock, 1, 0, ctx->timeout) != 1) { |
|||
memc_log (ctx, __LINE__, "memc_write: timeout waiting reply"); |
|||
return SERVER_TIMEOUT; |
|||
} |
|||
iov[0].iov_base = &header; |
|||
iov[0].iov_len = sizeof (struct memc_udp_header); |
|||
iov[1].iov_base = read_buf; |
|||
iov[1].iov_len = READ_BUFSIZ; |
|||
if ((r = readv (ctx->sock, iov, 2)) == -1) { |
|||
return SERVER_ERROR; |
|||
} |
|||
if (header.req_id != ctx->count && retries < MAX_RETRIES) { |
|||
retries ++; |
|||
/* Not our reply packet */ |
|||
continue; |
|||
} |
|||
break; |
|||
} |
|||
if (ctx->protocol != UDP_TEXT) { |
|||
if (poll_d (ctx->sock, 1, 0, ctx->timeout) != 1) { |
|||
memc_log (ctx, __LINE__, "memc_write: timeout waiting reply"); |
|||
return SERVER_TIMEOUT; |
|||
} |
|||
r = read (ctx->sock, read_buf, READ_BUFSIZ - 1); |
|||
} |
|||
/* Increment count */ |
|||
ctx->count++; |
|||
|
|||
if (strncmp (read_buf, STORED_TRAILER, sizeof (STORED_TRAILER) - 1) == 0) { |
|||
continue; |
|||
} |
|||
else if (strncmp (read_buf, NOT_STORED_TRAILER, sizeof (NOT_STORED_TRAILER) - 1) == 0) { |
|||
return CLIENT_ERROR; |
|||
} |
|||
else if (strncmp (read_buf, EXISTS_TRAILER, sizeof (EXISTS_TRAILER) - 1) == 0) { |
|||
return EXISTS; |
|||
} |
|||
else { |
|||
return SERVER_ERROR; |
|||
} |
|||
} |
|||
|
|||
return OK; |
|||
} |
|||
/* |
|||
* Delete command handler |
|||
*/ |
|||
memc_error_t |
|||
memc_delete (memcached_ctx_t *ctx, memcached_param_t *params, size_t *nelem) |
|||
{ |
|||
char read_buf[READ_BUFSIZ]; |
|||
int i, retries; |
|||
ssize_t r; |
|||
struct memc_udp_header header; |
|||
struct iovec iov[2]; |
|||
|
|||
for (i = 0; i < *nelem; i++) { |
|||
if (ctx->protocol == UDP_TEXT) { |
|||
/* Send udp header */ |
|||
bzero (&header, sizeof (header)); |
|||
header.dg_sent = htons(1); |
|||
header.req_id = ctx->count; |
|||
} |
|||
|
|||
r = snprintf (read_buf, READ_BUFSIZ, "delete %s" CRLF, params[i].key); |
|||
memc_log (ctx, __LINE__, "memc_delete: send delete request to memcached: %s", read_buf); |
|||
if (ctx->protocol == UDP_TEXT) { |
|||
iov[0].iov_base = &header; |
|||
iov[0].iov_len = sizeof (struct memc_udp_header); |
|||
iov[1].iov_base = read_buf; |
|||
iov[1].iov_len = r; |
|||
writev (ctx->sock, iov, 2); |
|||
} |
|||
else { |
|||
write (ctx->sock, read_buf, r); |
|||
} |
|||
|
|||
/* Read reply from server */ |
|||
retries = 0; |
|||
while (ctx->protocol == UDP_TEXT) { |
|||
if (poll_d (ctx->sock, 1, 0, ctx->timeout) != 1) { |
|||
return SERVER_TIMEOUT; |
|||
} |
|||
iov[0].iov_base = &header; |
|||
iov[0].iov_len = sizeof (struct memc_udp_header); |
|||
iov[1].iov_base = read_buf; |
|||
iov[1].iov_len = READ_BUFSIZ; |
|||
if ((r = readv (ctx->sock, iov, 2)) == -1) { |
|||
return SERVER_ERROR; |
|||
} |
|||
if (header.req_id != ctx->count && retries < MAX_RETRIES) { |
|||
retries ++; |
|||
/* Not our reply packet */ |
|||
continue; |
|||
} |
|||
break; |
|||
} |
|||
if (ctx->protocol != UDP_TEXT) { |
|||
if (poll_d (ctx->sock, 1, 0, ctx->timeout) != 1) { |
|||
return SERVER_TIMEOUT; |
|||
} |
|||
r = read (ctx->sock, read_buf, READ_BUFSIZ - 1); |
|||
} |
|||
|
|||
/* Increment count */ |
|||
ctx->count++; |
|||
if (strncmp (read_buf, DELETED_TRAILER, sizeof (DELETED_TRAILER) - 1) == 0) { |
|||
continue; |
|||
} |
|||
else if (strncmp (read_buf, NOT_FOUND_TRAILER, sizeof (NOT_FOUND_TRAILER) - 1) == 0) { |
|||
return NOT_EXISTS; |
|||
} |
|||
else { |
|||
return SERVER_ERROR; |
|||
} |
|||
} |
|||
|
|||
return OK; |
|||
} |
|||
|
|||
/* |
|||
* Write handler for memcached mirroring |
|||
* writing is done to each memcached server |
|||
*/ |
|||
memc_error_t |
|||
memc_write_mirror (memcached_ctx_t *ctx, size_t memcached_num, const char *cmd, memcached_param_t *params, size_t *nelem, int expire) |
|||
{ |
|||
memc_error_t r, result = OK; |
|||
|
|||
while (memcached_num --) { |
|||
if (ctx[memcached_num].alive == 1) { |
|||
r = memc_write (&ctx[memcached_num], cmd, params, nelem, expire); |
|||
if (r != OK) { |
|||
memc_log (&ctx[memcached_num], __LINE__, "memc_write_mirror: cannot write to mirror server: %s", memc_strerror (r)); |
|||
result = r; |
|||
ctx[memcached_num].alive = 0; |
|||
} |
|||
} |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
/* |
|||
* Read handler for memcached mirroring |
|||
* reading is done from first active memcached server |
|||
*/ |
|||
memc_error_t |
|||
memc_read_mirror (memcached_ctx_t *ctx, size_t memcached_num, const char *cmd, memcached_param_t *params, size_t *nelem) |
|||
{ |
|||
memc_error_t r, result = OK; |
|||
|
|||
while (memcached_num --) { |
|||
if (ctx[memcached_num].alive == 1) { |
|||
r = memc_read (&ctx[memcached_num], cmd, params, nelem); |
|||
if (r != OK) { |
|||
result = r; |
|||
if (r != NOT_EXISTS) { |
|||
ctx[memcached_num].alive = 0; |
|||
memc_log (&ctx[memcached_num], __LINE__, "memc_read_mirror: cannot write read from mirror server: %s", memc_strerror (r)); |
|||
} |
|||
else { |
|||
memc_log (&ctx[memcached_num], __LINE__, "memc_read_mirror: record not exists", memc_strerror (r)); |
|||
} |
|||
} |
|||
else { |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
/* |
|||
* Delete handler for memcached mirroring |
|||
* deleting is done for each active memcached server |
|||
*/ |
|||
memc_error_t |
|||
memc_delete_mirror (memcached_ctx_t *ctx, size_t memcached_num, const char *cmd, memcached_param_t *params, size_t *nelem) |
|||
{ |
|||
memc_error_t r, result = OK; |
|||
|
|||
while (memcached_num --) { |
|||
if (ctx[memcached_num].alive == 1) { |
|||
r = memc_delete (&ctx[memcached_num], params, nelem); |
|||
if (r != OK) { |
|||
result = r; |
|||
if (r != NOT_EXISTS) { |
|||
ctx[memcached_num].alive = 0; |
|||
memc_log (&ctx[memcached_num], __LINE__, "memc_delete_mirror: cannot delete from mirror server: %s", memc_strerror (r)); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
|
|||
/* |
|||
* Initialize memcached context for specified protocol |
|||
*/ |
|||
int |
|||
memc_init_ctx (memcached_ctx_t *ctx) |
|||
{ |
|||
if (ctx == NULL) { |
|||
return -1; |
|||
} |
|||
|
|||
ctx->count = 0; |
|||
ctx->alive = 1; |
|||
|
|||
switch (ctx->protocol) { |
|||
case UDP_TEXT: |
|||
return memc_make_udp_sock (ctx); |
|||
break; |
|||
case TCP_TEXT: |
|||
return memc_make_tcp_sock (ctx); |
|||
break; |
|||
/* Not implemented */ |
|||
case UDP_BIN: |
|||
case TCP_BIN: |
|||
default: |
|||
return -1; |
|||
} |
|||
} |
|||
/* |
|||
* Mirror init |
|||
*/ |
|||
int |
|||
memc_init_ctx_mirror (memcached_ctx_t *ctx, size_t memcached_num) |
|||
{ |
|||
int r, result = -1; |
|||
while (memcached_num--) { |
|||
if (ctx[memcached_num].alive == 1) { |
|||
r = memc_init_ctx (&ctx[memcached_num]); |
|||
if (r == -1) { |
|||
ctx[memcached_num].alive = 0; |
|||
memc_log (&ctx[memcached_num], __LINE__, "memc_init_ctx_mirror: cannot connect to server"); |
|||
} |
|||
else { |
|||
result = 1; |
|||
} |
|||
} |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
/* |
|||
* Close context connection |
|||
*/ |
|||
int |
|||
memc_close_ctx (memcached_ctx_t *ctx) |
|||
{ |
|||
if (ctx != NULL && ctx->sock != -1) { |
|||
return close (ctx->sock); |
|||
} |
|||
|
|||
return -1; |
|||
} |
|||
/* |
|||
* Mirror close |
|||
*/ |
|||
int |
|||
memc_close_ctx_mirror (memcached_ctx_t *ctx, size_t memcached_num) |
|||
{ |
|||
int r = 0; |
|||
while (memcached_num--) { |
|||
if (ctx[memcached_num].alive == 1) { |
|||
r = memc_close_ctx (&ctx[memcached_num]); |
|||
if (r == -1) { |
|||
memc_log (&ctx[memcached_num], __LINE__, "memc_close_ctx_mirror: cannot close connection to server properly"); |
|||
ctx[memcached_num].alive = 0; |
|||
} |
|||
} |
|||
} |
|||
|
|||
return r; |
|||
} |
|||
|
|||
|
|||
const char * memc_strerror (memc_error_t err) |
|||
{ |
|||
const char *p; |
|||
|
|||
switch (err) { |
|||
case OK: |
|||
p = "Ok"; |
|||
break; |
|||
case BAD_COMMAND: |
|||
p = "Bad command"; |
|||
break; |
|||
case CLIENT_ERROR: |
|||
p = "Client error"; |
|||
break; |
|||
case SERVER_ERROR: |
|||
p = "Server error"; |
|||
break; |
|||
case SERVER_TIMEOUT: |
|||
p = "Server timeout"; |
|||
break; |
|||
case NOT_EXISTS: |
|||
p = "Key not found"; |
|||
break; |
|||
case EXISTS: |
|||
p = "Key already exists"; |
|||
break; |
|||
case WRONG_LENGTH: |
|||
p = "Wrong result length"; |
|||
break; |
|||
default: |
|||
p = "Unknown error"; |
|||
break; |
|||
} |
|||
|
|||
return p; |
|||
} |
|||
|
|||
/* |
|||
* vi:ts=4 |
|||
*/ |
|||
@ -0,0 +1,113 @@ |
|||
#ifndef MEMCACHED_H |
|||
#define MEMCACHED_H |
|||
|
|||
#include <sys/types.h> |
|||
#include <netinet/in.h> |
|||
|
|||
#define MAXKEYLEN 250 |
|||
|
|||
#define MEMC_OPT_DEBUG 0x1 |
|||
|
|||
typedef enum memc_error { |
|||
OK, |
|||
BAD_COMMAND, |
|||
CLIENT_ERROR, |
|||
SERVER_ERROR, |
|||
SERVER_TIMEOUT, |
|||
NOT_EXISTS, |
|||
EXISTS, |
|||
WRONG_LENGTH |
|||
} memc_error_t; |
|||
|
|||
/* XXX: Only UDP_TEXT is supported at present */ |
|||
typedef enum memc_proto { |
|||
UDP_TEXT, |
|||
TCP_TEXT, |
|||
UDP_BIN, |
|||
TCP_BIN |
|||
} memc_proto_t; |
|||
|
|||
/* Port must be in network byte order */ |
|||
typedef struct memcached_ctx_s { |
|||
memc_proto_t protocol; |
|||
struct in_addr addr; |
|||
uint16_t port; |
|||
int sock; |
|||
int timeout; |
|||
/* Counter that is used for memcached operations in network byte order */ |
|||
uint16_t count; |
|||
/* Flag that signalize that this memcached is alive */ |
|||
short alive; |
|||
/* Options that can be specified for memcached connection */ |
|||
short options; |
|||
} memcached_ctx_t; |
|||
|
|||
typedef struct memcached_param_s { |
|||
char key[MAXKEYLEN]; |
|||
u_char *buf; |
|||
size_t bufsize; |
|||
} memcached_param_t; |
|||
|
|||
/* |
|||
* Initialize connection to memcached server: |
|||
* addr, port and timeout fields in ctx must be filled with valid values |
|||
* Return: |
|||
* 0 - success |
|||
* -1 - error (error is stored in errno) |
|||
*/ |
|||
int memc_init_ctx (memcached_ctx_t *ctx); |
|||
int memc_init_ctx_mirror (memcached_ctx_t *ctx, size_t memcached_num); |
|||
/* |
|||
* Memcached function for getting, setting, adding values to memcached server |
|||
* ctx - valid memcached context |
|||
* key - key to extract (max 250 characters as it specified in memcached API) |
|||
* buf, elemsize, nelem - allocated buffer of length nelem structures each of elemsize |
|||
* that would contain extracted data (NOT NULL TERMINATED) |
|||
* Return: |
|||
* memc_error_t |
|||
* nelem is changed according to actual number of extracted data |
|||
* |
|||
* "set" means "store this data". |
|||
* |
|||
* "add" means "store this data, but only if the server *doesn't* already |
|||
* hold data for this key". |
|||
|
|||
* "replace" means "store this data, but only if the server *does* |
|||
* already hold data for this key". |
|||
|
|||
* "append" means "add this data to an existing key after existing data". |
|||
|
|||
* "prepend" means "add this data to an existing key before existing data". |
|||
*/ |
|||
#define memc_get(ctx, params, nelem) memc_read(ctx, "get", params, nelem) |
|||
#define memc_set(ctx, params, nelem, expire) memc_write(ctx, "set", params, nelem, expire) |
|||
#define memc_add(ctx, params, nelem, expire) memc_write(ctx, "add", params, nelem, expire) |
|||
#define memc_replace(ctx, params, nelem, expire) memc_write(ctx, "replace", params, nelem, expire) |
|||
#define memc_append(ctx, params, nelem, expire) memc_write(ctx, "append", params, nelem, expire) |
|||
#define memc_prepend(ctx, params, nelem, expire) memc_write(ctx, "prepend", params, nelem, expire) |
|||
|
|||
/* Functions that works with mirror of memcached servers */ |
|||
#define memc_get_mirror(ctx, num, params, nelem) memc_read_mirror(ctx, num, "get", params, nelem) |
|||
#define memc_set_mirror(ctx, num, params, nelem, expire) memc_write_mirror(ctx, num, "set", params, nelem, expire) |
|||
#define memc_add_mirror(ctx, num, params, nelem, expire) memc_write_mirror(ctx, num, "add", params, nelem, expire) |
|||
#define memc_replace_mirror(ctx, num, params, nelem, expire) memc_write_mirror(ctx, num, "replace", params, nelem, expire) |
|||
#define memc_append_mirror(ctx, num, params, nelem, expire) memc_write_mirror(ctx, num, "append", params, nelem, expire) |
|||
#define memc_prepend_mirror(ctx, num, params, nelem, expire) memc_write_mirror(ctx, num, "prepend", params, nelem, expire) |
|||
|
|||
|
|||
memc_error_t memc_read (memcached_ctx_t *ctx, const char *cmd, memcached_param_t *params, size_t *nelem); |
|||
memc_error_t memc_write (memcached_ctx_t *ctx, const char *cmd, memcached_param_t *params, size_t *nelem, int expire); |
|||
memc_error_t memc_delete (memcached_ctx_t *ctx, memcached_param_t *params, size_t *nelem); |
|||
|
|||
memc_error_t memc_write_mirror (memcached_ctx_t *ctx, size_t memcached_num, const char *cmd, memcached_param_t *params, size_t *nelem, int expire); |
|||
memc_error_t memc_read_mirror (memcached_ctx_t *ctx, size_t memcached_num, const char *cmd, memcached_param_t *params, size_t *nelem); |
|||
memc_error_t memc_delete_mirror (memcached_ctx_t *ctx, size_t memcached_num, const char *cmd, memcached_param_t *params, size_t *nelem); |
|||
|
|||
/* Return symbolic name of memcached error*/ |
|||
const char * memc_strerror (memc_error_t err); |
|||
|
|||
/* Destroy socket from ctx */ |
|||
int memc_close_ctx (memcached_ctx_t *ctx); |
|||
int memc_close_ctx_mirror (memcached_ctx_t *ctx, size_t memcached_num); |
|||
|
|||
#endif |
|||
@ -0,0 +1,521 @@ |
|||
#ifdef _THREAD_SAFE |
|||
#include <pthread.h> |
|||
#endif |
|||
|
|||
#include <sys/types.h> |
|||
#include <time.h> |
|||
#include <stdlib.h> |
|||
#include <stdio.h> |
|||
#ifdef HAVE_STDINT_H |
|||
#include <stdint.h> |
|||
#endif |
|||
#ifdef HAVE_INTTYPES_H |
|||
#include <inttypes.h> |
|||
#endif |
|||
#include <limits.h> |
|||
#ifdef WITH_DEBUG |
|||
#include <syslog.h> |
|||
#endif |
|||
#include "upstream.h" |
|||
|
|||
#ifdef WITH_DEBUG |
|||
#define msg_debug(args...) syslog(LOG_DEBUG, ##args) |
|||
#else |
|||
#define msg_debug(args...) do {} while(0) |
|||
#endif |
|||
|
|||
#ifdef _THREAD_SAFE |
|||
pthread_rwlock_t upstream_mtx = PTHREAD_RWLOCK_INITIALIZER; |
|||
#define U_RLOCK() do { pthread_rwlock_rdlock (&upstream_mtx); } while (0) |
|||
#define U_WLOCK() do { pthread_rwlock_wrlock (&upstream_mtx); } while (0) |
|||
#define U_UNLOCK() do { pthread_rwlock_unlock (&upstream_mtx); } while (0) |
|||
#else |
|||
#define U_RLOCK() do {} while (0) |
|||
#define U_WLOCK() do {} while (0) |
|||
#define U_UNLOCK() do {} while (0) |
|||
#endif |
|||
|
|||
#define MAX_TRIES 20 |
|||
|
|||
/* |
|||
* Poly: 0xedb88320 |
|||
* Init: 0x0 |
|||
*/ |
|||
|
|||
static const uint32_t crc32lookup[256] = { |
|||
0x00000000U, 0x77073096U, 0xee0e612cU, 0x990951baU, 0x076dc419U, 0x706af48fU, |
|||
0xe963a535U, 0x9e6495a3U, 0x0edb8832U, 0x79dcb8a4U, 0xe0d5e91eU, 0x97d2d988U, |
|||
0x09b64c2bU, 0x7eb17cbdU, 0xe7b82d07U, 0x90bf1d91U, 0x1db71064U, 0x6ab020f2U, |
|||
0xf3b97148U, 0x84be41deU, 0x1adad47dU, 0x6ddde4ebU, 0xf4d4b551U, 0x83d385c7U, |
|||
0x136c9856U, 0x646ba8c0U, 0xfd62f97aU, 0x8a65c9ecU, 0x14015c4fU, 0x63066cd9U, |
|||
0xfa0f3d63U, 0x8d080df5U, 0x3b6e20c8U, 0x4c69105eU, 0xd56041e4U, 0xa2677172U, |
|||
0x3c03e4d1U, 0x4b04d447U, 0xd20d85fdU, 0xa50ab56bU, 0x35b5a8faU, 0x42b2986cU, |
|||
0xdbbbc9d6U, 0xacbcf940U, 0x32d86ce3U, 0x45df5c75U, 0xdcd60dcfU, 0xabd13d59U, |
|||
0x26d930acU, 0x51de003aU, 0xc8d75180U, 0xbfd06116U, 0x21b4f4b5U, 0x56b3c423U, |
|||
0xcfba9599U, 0xb8bda50fU, 0x2802b89eU, 0x5f058808U, 0xc60cd9b2U, 0xb10be924U, |
|||
0x2f6f7c87U, 0x58684c11U, 0xc1611dabU, 0xb6662d3dU, 0x76dc4190U, 0x01db7106U, |
|||
0x98d220bcU, 0xefd5102aU, 0x71b18589U, 0x06b6b51fU, 0x9fbfe4a5U, 0xe8b8d433U, |
|||
0x7807c9a2U, 0x0f00f934U, 0x9609a88eU, 0xe10e9818U, 0x7f6a0dbbU, 0x086d3d2dU, |
|||
0x91646c97U, 0xe6635c01U, 0x6b6b51f4U, 0x1c6c6162U, 0x856530d8U, 0xf262004eU, |
|||
0x6c0695edU, 0x1b01a57bU, 0x8208f4c1U, 0xf50fc457U, 0x65b0d9c6U, 0x12b7e950U, |
|||
0x8bbeb8eaU, 0xfcb9887cU, 0x62dd1ddfU, 0x15da2d49U, 0x8cd37cf3U, 0xfbd44c65U, |
|||
0x4db26158U, 0x3ab551ceU, 0xa3bc0074U, 0xd4bb30e2U, 0x4adfa541U, 0x3dd895d7U, |
|||
0xa4d1c46dU, 0xd3d6f4fbU, 0x4369e96aU, 0x346ed9fcU, 0xad678846U, 0xda60b8d0U, |
|||
0x44042d73U, 0x33031de5U, 0xaa0a4c5fU, 0xdd0d7cc9U, 0x5005713cU, 0x270241aaU, |
|||
0xbe0b1010U, 0xc90c2086U, 0x5768b525U, 0x206f85b3U, 0xb966d409U, 0xce61e49fU, |
|||
0x5edef90eU, 0x29d9c998U, 0xb0d09822U, 0xc7d7a8b4U, 0x59b33d17U, 0x2eb40d81U, |
|||
0xb7bd5c3bU, 0xc0ba6cadU, 0xedb88320U, 0x9abfb3b6U, 0x03b6e20cU, 0x74b1d29aU, |
|||
0xead54739U, 0x9dd277afU, 0x04db2615U, 0x73dc1683U, 0xe3630b12U, 0x94643b84U, |
|||
0x0d6d6a3eU, 0x7a6a5aa8U, 0xe40ecf0bU, 0x9309ff9dU, 0x0a00ae27U, 0x7d079eb1U, |
|||
0xf00f9344U, 0x8708a3d2U, 0x1e01f268U, 0x6906c2feU, 0xf762575dU, 0x806567cbU, |
|||
0x196c3671U, 0x6e6b06e7U, 0xfed41b76U, 0x89d32be0U, 0x10da7a5aU, 0x67dd4accU, |
|||
0xf9b9df6fU, 0x8ebeeff9U, 0x17b7be43U, 0x60b08ed5U, 0xd6d6a3e8U, 0xa1d1937eU, |
|||
0x38d8c2c4U, 0x4fdff252U, 0xd1bb67f1U, 0xa6bc5767U, 0x3fb506ddU, 0x48b2364bU, |
|||
0xd80d2bdaU, 0xaf0a1b4cU, 0x36034af6U, 0x41047a60U, 0xdf60efc3U, 0xa867df55U, |
|||
0x316e8eefU, 0x4669be79U, 0xcb61b38cU, 0xbc66831aU, 0x256fd2a0U, 0x5268e236U, |
|||
0xcc0c7795U, 0xbb0b4703U, 0x220216b9U, 0x5505262fU, 0xc5ba3bbeU, 0xb2bd0b28U, |
|||
0x2bb45a92U, 0x5cb36a04U, 0xc2d7ffa7U, 0xb5d0cf31U, 0x2cd99e8bU, 0x5bdeae1dU, |
|||
0x9b64c2b0U, 0xec63f226U, 0x756aa39cU, 0x026d930aU, 0x9c0906a9U, 0xeb0e363fU, |
|||
0x72076785U, 0x05005713U, 0x95bf4a82U, 0xe2b87a14U, 0x7bb12baeU, 0x0cb61b38U, |
|||
0x92d28e9bU, 0xe5d5be0dU, 0x7cdcefb7U, 0x0bdbdf21U, 0x86d3d2d4U, 0xf1d4e242U, |
|||
0x68ddb3f8U, 0x1fda836eU, 0x81be16cdU, 0xf6b9265bU, 0x6fb077e1U, 0x18b74777U, |
|||
0x88085ae6U, 0xff0f6a70U, 0x66063bcaU, 0x11010b5cU, 0x8f659effU, 0xf862ae69U, |
|||
0x616bffd3U, 0x166ccf45U, 0xa00ae278U, 0xd70dd2eeU, 0x4e048354U, 0x3903b3c2U, |
|||
0xa7672661U, 0xd06016f7U, 0x4969474dU, 0x3e6e77dbU, 0xaed16a4aU, 0xd9d65adcU, |
|||
0x40df0b66U, 0x37d83bf0U, 0xa9bcae53U, 0xdebb9ec5U, 0x47b2cf7fU, 0x30b5ffe9U, |
|||
0xbdbdf21cU, 0xcabac28aU, 0x53b39330U, 0x24b4a3a6U, 0xbad03605U, 0xcdd70693U, |
|||
0x54de5729U, 0x23d967bfU, 0xb3667a2eU, 0xc4614ab8U, 0x5d681b02U, 0x2a6f2b94U, |
|||
0xb40bbe37U, 0xc30c8ea1U, 0x5a05df1bU, 0x2d02ef8dU |
|||
}; |
|||
|
|||
/* |
|||
* Check upstream parameters and mark it whether valid or dead |
|||
*/ |
|||
static void |
|||
check_upstream (struct upstream *up, time_t now, time_t error_timeout, time_t revive_timeout, size_t max_errors) |
|||
{ |
|||
if (up->dead) { |
|||
if (now - up->time >= revive_timeout) { |
|||
msg_debug ("check_upstream: reviving upstream after %ld seconds", (long int) now - up->time); |
|||
U_WLOCK (); |
|||
up->dead = 0; |
|||
up->errors = 0; |
|||
up->time = 0; |
|||
up->weight = up->priority; |
|||
U_UNLOCK (); |
|||
} |
|||
} |
|||
else { |
|||
if (now - up->time >= error_timeout && up->errors >= max_errors) { |
|||
msg_debug ("check_upstream: marking upstreams as dead after %ld errors", (long int) up->errors); |
|||
U_WLOCK (); |
|||
up->dead = 1; |
|||
up->time = now; |
|||
up->weight = 0; |
|||
U_UNLOCK (); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/* |
|||
* Call this function after failed upstream request |
|||
*/ |
|||
void |
|||
upstream_fail (struct upstream *up, time_t now) |
|||
{ |
|||
if (up->time != 0) { |
|||
up->errors ++; |
|||
} |
|||
else { |
|||
U_WLOCK (); |
|||
up->time = now; |
|||
up->errors ++; |
|||
U_UNLOCK (); |
|||
} |
|||
} |
|||
/* |
|||
* Call this function after successfull upstream request |
|||
*/ |
|||
void |
|||
upstream_ok (struct upstream *up, time_t now) |
|||
{ |
|||
if (up->errors != 0) { |
|||
U_WLOCK (); |
|||
up->errors = 0; |
|||
up->time = 0; |
|||
U_UNLOCK (); |
|||
} |
|||
|
|||
up->weight --; |
|||
} |
|||
/* |
|||
* Mark all upstreams as active. This function is used when all upstreams are marked as inactive |
|||
*/ |
|||
void |
|||
revive_all_upstreams (void *ups, size_t members, size_t msize) |
|||
{ |
|||
int i; |
|||
struct upstream *cur; |
|||
u_char *p; |
|||
|
|||
U_WLOCK (); |
|||
msg_debug ("revive_all_upstreams: starting reviving all upstreams"); |
|||
p = ups; |
|||
for (i = 0; i < members; i++) { |
|||
cur = (struct upstream *)p; |
|||
cur->time = 0; |
|||
cur->errors = 0; |
|||
cur->dead = 0; |
|||
cur->weight = cur->priority; |
|||
p += msize; |
|||
} |
|||
U_UNLOCK (); |
|||
} |
|||
|
|||
/* |
|||
* Scan all upstreams for errors and mark upstreams dead or alive depends on conditions, |
|||
* return number of alive upstreams |
|||
*/ |
|||
static int |
|||
rescan_upstreams (void *ups, size_t members, size_t msize, time_t now, time_t error_timeout, time_t revive_timeout, size_t max_errors) |
|||
{ |
|||
int i, alive; |
|||
struct upstream *cur; |
|||
u_char *p; |
|||
|
|||
/* Recheck all upstreams */ |
|||
p = ups; |
|||
alive = members; |
|||
for (i = 0; i < members; i++) { |
|||
cur = (struct upstream *)p; |
|||
check_upstream (cur, now, error_timeout, revive_timeout, max_errors); |
|||
alive -= cur->dead; |
|||
p += msize; |
|||
} |
|||
|
|||
/* All upstreams are dead */ |
|||
if (alive == 0) { |
|||
revive_all_upstreams (ups, members, msize); |
|||
alive = members; |
|||
} |
|||
|
|||
msg_debug ("rescan_upstreams: %d upstreams alive", alive); |
|||
|
|||
return alive; |
|||
|
|||
} |
|||
|
|||
/* Return alive upstream by its number */ |
|||
static struct upstream * |
|||
get_upstream_by_number (void *ups, size_t members, size_t msize, int selected) |
|||
{ |
|||
int i; |
|||
u_char *p, *c; |
|||
struct upstream *cur; |
|||
|
|||
i = 0; |
|||
p = ups; |
|||
c = ups; |
|||
U_RLOCK (); |
|||
for (;;) { |
|||
/* Out of range, return NULL */ |
|||
if (p > c + members * msize) { |
|||
break; |
|||
} |
|||
|
|||
cur = (struct upstream *)p; |
|||
p += msize; |
|||
|
|||
if (cur->dead) { |
|||
/* Skip inactive upstreams */ |
|||
continue; |
|||
} |
|||
/* Return selected upstream */ |
|||
if (i == selected) { |
|||
U_UNLOCK (); |
|||
return cur; |
|||
} |
|||
i++; |
|||
} |
|||
U_UNLOCK (); |
|||
|
|||
/* Error */ |
|||
return NULL; |
|||
|
|||
} |
|||
|
|||
/* |
|||
* Get hash key for specified key (perl hash) |
|||
*/ |
|||
static uint32_t |
|||
get_hash_for_key (uint32_t hash, char *key, size_t keylen) |
|||
{ |
|||
uint32_t h, index; |
|||
const char *end = key + keylen; |
|||
|
|||
h = ~hash; |
|||
|
|||
while (key < end) { |
|||
index = (h ^ (u_char) *key) & 0x000000ffU; |
|||
h = (h >> 8) ^ crc32lookup[index]; |
|||
++key; |
|||
} |
|||
|
|||
return (~h); |
|||
} |
|||
|
|||
/* |
|||
* Recheck all upstreams and return random active upstream |
|||
*/ |
|||
struct upstream * |
|||
get_random_upstream (void *ups, size_t members, size_t msize, time_t now, time_t error_timeout, time_t revive_timeout, size_t max_errors) |
|||
{ |
|||
int alive, selected; |
|||
|
|||
alive = rescan_upstreams (ups, members, msize, now, error_timeout, revive_timeout, max_errors); |
|||
selected = rand () % alive; |
|||
msg_debug ("get_random_upstream: return upstream with number %d of %d", selected, alive); |
|||
|
|||
return get_upstream_by_number (ups, members, msize, selected); |
|||
} |
|||
|
|||
/* |
|||
* Return upstream by hash, that is calculated from active upstreams number |
|||
*/ |
|||
struct upstream * |
|||
get_upstream_by_hash (void *ups, size_t members, size_t msize, time_t now, |
|||
time_t error_timeout, time_t revive_timeout, size_t max_errors, |
|||
char *key, size_t keylen) |
|||
{ |
|||
int alive, tries = 0, r; |
|||
uint32_t h = 0, ht; |
|||
char *p, numbuf[4]; |
|||
struct upstream *cur; |
|||
|
|||
alive = rescan_upstreams (ups, members, msize, now, error_timeout, revive_timeout, max_errors); |
|||
|
|||
if (alive == 0) { |
|||
return NULL; |
|||
} |
|||
|
|||
h = get_hash_for_key (0, key, keylen); |
|||
#ifdef HASH_COMPAT |
|||
h = (h >> 16) & 0x7fff; |
|||
#endif |
|||
h %= members; |
|||
msg_debug ("get_upstream_by_hash: try to select upstream number %d of %zd", h, members); |
|||
|
|||
for (;;) { |
|||
p = (char *)ups + msize * h; |
|||
cur = (struct upstream *)p; |
|||
if (!cur->dead) { |
|||
break; |
|||
} |
|||
r = snprintf (numbuf, sizeof (numbuf), "%d", tries); |
|||
ht = get_hash_for_key (0, numbuf, r); |
|||
ht = get_hash_for_key (ht, key, keylen); |
|||
#ifdef HASH_COMPAT |
|||
h += (ht >> 16) & 0x7fff; |
|||
#else |
|||
h += ht; |
|||
#endif |
|||
h %= members; |
|||
msg_debug ("get_upstream_by_hash: try to select upstream number %d of %zd, tries: %d", h, members, tries); |
|||
tries ++; |
|||
if (tries > MAX_TRIES) { |
|||
msg_debug ("get_upstream_by_hash: max tries exceed, returning NULL"); |
|||
return NULL; |
|||
} |
|||
} |
|||
|
|||
U_RLOCK (); |
|||
p = ups; |
|||
U_UNLOCK (); |
|||
return cur; |
|||
} |
|||
|
|||
/* |
|||
* Recheck all upstreams and return upstream in round-robin order according to weight and priority |
|||
*/ |
|||
struct upstream * |
|||
get_upstream_round_robin (void *ups, size_t members, size_t msize, time_t now, time_t error_timeout, time_t revive_timeout, size_t max_errors) |
|||
{ |
|||
int alive, max_weight, i; |
|||
struct upstream *cur, *selected = NULL; |
|||
u_char *p; |
|||
|
|||
/* Recheck all upstreams */ |
|||
alive = rescan_upstreams (ups, members, msize, now, error_timeout, revive_timeout, max_errors); |
|||
|
|||
p = ups; |
|||
max_weight = 0; |
|||
selected = (struct upstream *)p; |
|||
U_RLOCK (); |
|||
for (i = 0; i < members; i++) { |
|||
cur = (struct upstream *)p; |
|||
if (!cur->dead) { |
|||
if (max_weight < cur->weight) { |
|||
max_weight = cur->weight; |
|||
selected = cur; |
|||
} |
|||
} |
|||
p += msize; |
|||
} |
|||
U_UNLOCK (); |
|||
|
|||
if (max_weight == 0) { |
|||
p = ups; |
|||
U_WLOCK (); |
|||
for (i = 0; i < members; i++) { |
|||
cur = (struct upstream *)p; |
|||
cur->weight = cur->priority; |
|||
if (!cur->dead) { |
|||
if (max_weight < cur->priority) { |
|||
max_weight = cur->priority; |
|||
selected = cur; |
|||
} |
|||
} |
|||
p += msize; |
|||
} |
|||
U_UNLOCK (); |
|||
} |
|||
msg_debug ("get_upstream_round_robin: selecting upstream with weight %d", max_weight); |
|||
|
|||
return selected; |
|||
} |
|||
|
|||
/* |
|||
* Recheck all upstreams and return upstream in round-robin order according to only priority (master-slaves) |
|||
*/ |
|||
struct upstream * |
|||
get_upstream_master_slave (void *ups, size_t members, size_t msize, time_t now, time_t error_timeout, time_t revive_timeout, size_t max_errors) |
|||
{ |
|||
int alive, max_weight, i; |
|||
struct upstream *cur, *selected = NULL; |
|||
u_char *p; |
|||
|
|||
/* Recheck all upstreams */ |
|||
alive = rescan_upstreams (ups, members, msize, now, error_timeout, revive_timeout, max_errors); |
|||
|
|||
p = ups; |
|||
max_weight = 0; |
|||
selected = (struct upstream *)p; |
|||
U_RLOCK (); |
|||
for (i = 0; i < members; i++) { |
|||
cur = (struct upstream *)p; |
|||
if (!cur->dead) { |
|||
if (max_weight < cur->priority) { |
|||
max_weight = cur->priority; |
|||
selected = cur; |
|||
} |
|||
} |
|||
p += msize; |
|||
} |
|||
U_UNLOCK (); |
|||
msg_debug ("get_upstream_master_slave: selecting upstream with priority %d", max_weight); |
|||
|
|||
return selected; |
|||
} |
|||
|
|||
/* |
|||
* Ketama manipulation functions |
|||
*/ |
|||
|
|||
static int |
|||
ketama_sort_cmp (const void *a1, const void *a2) |
|||
{ |
|||
return *((uint32_t *)a1) - *((uint32_t *)a2); |
|||
} |
|||
|
|||
/* |
|||
* Add ketama points for specified upstream |
|||
*/ |
|||
int |
|||
upstream_ketama_add (struct upstream *up, char *up_key, size_t keylen, size_t keypoints) |
|||
{ |
|||
uint32_t h = 0; |
|||
char tmp[4]; |
|||
int i; |
|||
|
|||
/* Allocate ketama points array */ |
|||
if (up->ketama_points == NULL) { |
|||
up->ketama_points_size = keypoints; |
|||
up->ketama_points = malloc (sizeof (uint32_t) * up->ketama_points_size); |
|||
if (up->ketama_points == NULL) { |
|||
return -1; |
|||
} |
|||
} |
|||
|
|||
h = get_hash_for_key (h, up_key, keylen); |
|||
|
|||
for (i = 0; i < keypoints; i++) { |
|||
tmp[0] = i & 0xff; |
|||
tmp[1] = (i >> 8) & 0xff; |
|||
tmp[2] = (i >> 16) & 0xff; |
|||
tmp[3] = (i >> 24) & 0xff; |
|||
|
|||
h = get_hash_for_key (h, tmp, sizeof (tmp) * sizeof (char)); |
|||
up->ketama_points[i] = h; |
|||
} |
|||
/* Keep points sorted */ |
|||
qsort (up->ketama_points, keypoints, sizeof (uint32_t), ketama_sort_cmp); |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
/* |
|||
* Return upstream by hash and find nearest ketama point in some server |
|||
*/ |
|||
struct upstream * |
|||
get_upstream_by_hash_ketama (void *ups, size_t members, size_t msize, time_t now, |
|||
time_t error_timeout, time_t revive_timeout, size_t max_errors, |
|||
char *key, size_t keylen) |
|||
{ |
|||
int alive, i; |
|||
uint32_t h = 0, step, middle, d, min_diff = UINT_MAX; |
|||
char *p; |
|||
struct upstream *cur = NULL, *nearest = NULL; |
|||
|
|||
alive = rescan_upstreams (ups, members, msize, now, error_timeout, revive_timeout, max_errors); |
|||
|
|||
if (alive == 0) { |
|||
return NULL; |
|||
} |
|||
|
|||
h = get_hash_for_key (h, key, keylen); |
|||
|
|||
U_RLOCK (); |
|||
p = ups; |
|||
nearest = (struct upstream *)p; |
|||
for (i = 0; i < members; i++) { |
|||
cur = (struct upstream *)p; |
|||
if (!cur->dead && cur->ketama_points != NULL) { |
|||
/* Find nearest ketama point for this key */ |
|||
step = cur->ketama_points_size / 2; |
|||
middle = step; |
|||
while (step != 1) { |
|||
d = cur->ketama_points[middle] - h; |
|||
if (abs (d) < min_diff) { |
|||
min_diff = abs (d); |
|||
nearest = cur; |
|||
} |
|||
step /= 2; |
|||
if (d > 0) { |
|||
middle -= step; |
|||
} |
|||
else { |
|||
middle += step; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
U_UNLOCK (); |
|||
return nearest; |
|||
} |
|||
|
|||
#undef U_LOCK |
|||
#undef U_UNLOCK |
|||
#undef msg_debug |
|||
/* |
|||
* vi:ts=4 |
|||
*/ |
|||
@ -0,0 +1,43 @@ |
|||
#ifndef UPSTREAM_H |
|||
#define UPSTREAM_H |
|||
|
|||
#include <sys/types.h> |
|||
|
|||
|
|||
struct upstream { |
|||
unsigned int errors; |
|||
time_t time; |
|||
unsigned char dead; |
|||
unsigned char priority; |
|||
int16_t weight; |
|||
uint32_t *ketama_points; |
|||
size_t ketama_points_size; |
|||
}; |
|||
|
|||
void upstream_fail (struct upstream *up, time_t now); |
|||
void upstream_ok (struct upstream *up, time_t now); |
|||
void revive_all_upstreams (void *ups, size_t members, size_t msize); |
|||
int upstream_ketama_add (struct upstream *up, char *up_key, size_t keylen, size_t keypoints); |
|||
|
|||
struct upstream* get_random_upstream (void *ups, size_t members, size_t msize, |
|||
time_t now, time_t error_timeout, |
|||
time_t revive_timeout, size_t max_errors); |
|||
|
|||
struct upstream* get_upstream_by_hash (void *ups, size_t members, size_t msize, |
|||
time_t now, time_t error_timeout, |
|||
time_t revive_timeout, size_t max_errors, |
|||
char *key, size_t keylen); |
|||
|
|||
struct upstream* get_upstream_round_robin (void *ups, size_t members, size_t msize, |
|||
time_t now, time_t error_timeout, |
|||
time_t revive_timeout, size_t max_errors); |
|||
|
|||
struct upstream* get_upstream_by_hash_ketama (void *ups, size_t members, size_t msize, time_t now, |
|||
time_t error_timeout, time_t revive_timeout, size_t max_errors, |
|||
char *key, size_t keylen); |
|||
|
|||
|
|||
#endif /* UPSTREAM_H */ |
|||
/* |
|||
* vi:ts=4 |
|||
*/ |
|||
@ -0,0 +1,534 @@ |
|||
#include <sys/param.h> |
|||
#include <stdio.h> |
|||
#include <stdlib.h> |
|||
#include <string.h> |
|||
#include <time.h> |
|||
#include <fcntl.h> |
|||
#include <netdb.h> |
|||
#include <errno.h> |
|||
#include <unistd.h> |
|||
#ifdef HAVE_LIBUTIL_H |
|||
#include <libutil.h> |
|||
#endif |
|||
#include <stdarg.h> |
|||
#include <sys/file.h> |
|||
#include "util.h" |
|||
#include "cfg_file.h" |
|||
|
|||
int |
|||
event_make_socket_nonblocking (int fd) |
|||
{ |
|||
if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) { |
|||
return -1; |
|||
} |
|||
return 0; |
|||
} |
|||
|
|||
static int |
|||
make_socket_ai (struct addrinfo *ai) |
|||
{ |
|||
struct linger linger; |
|||
int fd, on = 1, r; |
|||
int serrno; |
|||
|
|||
/* Create listen socket */ |
|||
fd = socket(AF_INET, SOCK_STREAM, 0); |
|||
if (fd == -1) { |
|||
return (-1); |
|||
} |
|||
|
|||
if (event_make_socket_nonblocking(fd) < 0) |
|||
goto out; |
|||
|
|||
if (fcntl(fd, F_SETFD, 1) == -1) { |
|||
goto out; |
|||
} |
|||
|
|||
setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, sizeof(on)); |
|||
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on)); |
|||
linger.l_onoff = 1; |
|||
linger.l_linger = 5; |
|||
setsockopt(fd, SOL_SOCKET, SO_LINGER, (void *)&linger, sizeof(linger)); |
|||
|
|||
r = bind(fd, ai->ai_addr, ai->ai_addrlen); |
|||
|
|||
if (r == -1) { |
|||
if (errno != EINPROGRESS) { |
|||
goto out; |
|||
} |
|||
} |
|||
|
|||
return (fd); |
|||
|
|||
out: |
|||
serrno = errno; |
|||
close(fd); |
|||
errno = serrno; |
|||
return (-1); |
|||
} |
|||
|
|||
int |
|||
make_socket (const char *address, u_short port) |
|||
{ |
|||
int fd; |
|||
struct addrinfo ai, *aitop = NULL; |
|||
char strport[NI_MAXSERV]; |
|||
int ai_result; |
|||
|
|||
memset(&ai, 0, sizeof (ai)); |
|||
ai.ai_family = AF_INET; |
|||
ai.ai_socktype = SOCK_STREAM; |
|||
ai.ai_flags = AI_PASSIVE; |
|||
snprintf(strport, sizeof (strport), "%d", port); |
|||
if ((ai_result = getaddrinfo(address, strport, &ai, &aitop)) != 0) { |
|||
return (-1); |
|||
} |
|||
|
|||
fd = make_socket_ai(aitop); |
|||
|
|||
freeaddrinfo(aitop); |
|||
|
|||
return (fd); |
|||
} |
|||
|
|||
int |
|||
make_unix_socket (const char *path, struct sockaddr_un *addr) |
|||
{ |
|||
size_t len = strlen (path); |
|||
int sock; |
|||
|
|||
if (len > sizeof (addr->sun_path) - 1) return -1; |
|||
|
|||
#ifdef FREEBSD |
|||
addr->sun_len = sizeof (struct sockaddr_un); |
|||
#endif |
|||
|
|||
addr->sun_family = AF_UNIX; |
|||
|
|||
strncpy (addr->sun_path, path, len); |
|||
|
|||
sock = socket (PF_LOCAL, SOCK_STREAM, 0); |
|||
|
|||
if (sock != -1) { |
|||
if (bind (sock, (struct sockaddr *) addr, sizeof (struct sockaddr_un)) == -1) return -1; |
|||
} |
|||
|
|||
return sock; |
|||
} |
|||
|
|||
void |
|||
read_cmd_line (int argc, char **argv, struct config_file *cfg) |
|||
{ |
|||
int ch; |
|||
while ((ch = getopt(argc, argv, "hfc:")) != -1) { |
|||
switch (ch) { |
|||
case 'f': |
|||
cfg->no_fork = 1; |
|||
break; |
|||
case 'c': |
|||
if (optarg && cfg->cfg_name) { |
|||
free (cfg->cfg_name); |
|||
cfg->cfg_name = strdup (optarg); |
|||
} |
|||
break; |
|||
case 'h': |
|||
case '?': |
|||
default: |
|||
/* Show help message and exit */ |
|||
printf ("Rspamd version " RVERSION "\n" |
|||
"Usage: rspamd [-h] [-n] [-f] [-c config_file]\n" |
|||
"-h: This help message\n" |
|||
"-f: Do not daemonize main process\n" |
|||
"-c: Specify config file (./rspamd.conf is used by default)\n"); |
|||
exit (0); |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
|
|||
int |
|||
write_pid (struct rspamd_main *main) |
|||
{ |
|||
pid_t pid; |
|||
main->pfh = pidfile_open (main->cfg->pid_file, 0644, &pid); |
|||
|
|||
if (main->pfh == NULL) { |
|||
return -1; |
|||
} |
|||
|
|||
pidfile_write (main->pfh); |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
void |
|||
init_signals (struct sigaction *signals, sig_t sig_handler) |
|||
{ |
|||
/* Setting up signal handlers */ |
|||
/* SIGUSR1 - reopen config file */ |
|||
/* SIGUSR2 - worker is ready for accept */ |
|||
sigemptyset(&signals->sa_mask); |
|||
sigaddset(&signals->sa_mask, SIGTERM); |
|||
sigaddset(&signals->sa_mask, SIGINT); |
|||
sigaddset(&signals->sa_mask, SIGHUP); |
|||
sigaddset(&signals->sa_mask, SIGCHLD); |
|||
sigaddset(&signals->sa_mask, SIGUSR1); |
|||
sigaddset(&signals->sa_mask, SIGUSR2); |
|||
|
|||
|
|||
signals->sa_handler = sig_handler; |
|||
sigaction (SIGTERM, signals, NULL); |
|||
sigaction (SIGINT, signals, NULL); |
|||
sigaction (SIGHUP, signals, NULL); |
|||
sigaction (SIGCHLD, signals, NULL); |
|||
sigaction (SIGUSR1, signals, NULL); |
|||
sigaction (SIGUSR2, signals, NULL); |
|||
} |
|||
|
|||
void |
|||
pass_signal_worker (struct workq *workers, int signo) |
|||
{ |
|||
struct rspamd_worker *cur; |
|||
TAILQ_FOREACH (cur, workers, next) { |
|||
kill (cur->pid, signo); |
|||
} |
|||
} |
|||
|
|||
#ifndef HAVE_SETPROCTITLE |
|||
|
|||
static char *title_buffer = 0; |
|||
static size_t title_buffer_size = 0; |
|||
static char *title_progname, *title_progname_full; |
|||
|
|||
int |
|||
setproctitle(const char *fmt, ...) |
|||
{ |
|||
if (!title_buffer || !title_buffer_size) { |
|||
errno = ENOMEM; |
|||
return -1; |
|||
} |
|||
|
|||
memset (title_buffer, '\0', title_buffer_size); |
|||
|
|||
ssize_t written; |
|||
|
|||
if (fmt) { |
|||
ssize_t written2; |
|||
va_list ap; |
|||
|
|||
written = snprintf (title_buffer, title_buffer_size, "%s: ", title_progname); |
|||
if (written < 0 || (size_t) written >= title_buffer_size) |
|||
return -1; |
|||
|
|||
va_start (ap, fmt); |
|||
written2 = |
|||
vsnprintf (title_buffer + written, |
|||
title_buffer_size - written, fmt, ap); |
|||
va_end (ap); |
|||
if (written2 < 0 |
|||
|| (size_t) written2 >= title_buffer_size - written) |
|||
return -1; |
|||
} else { |
|||
written = |
|||
snprintf (title_buffer, title_buffer_size, "%s", |
|||
title_progname); |
|||
if (written < 0 || (size_t) written >= title_buffer_size) |
|||
return -1; |
|||
} |
|||
|
|||
written = strlen (title_buffer); |
|||
memset (title_buffer + written, '\0', title_buffer_size - written); |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
/* |
|||
It has to be _init function, because __attribute__((constructor)) |
|||
functions gets called without arguments. |
|||
*/ |
|||
|
|||
int |
|||
init_title(int argc, char *argv[], char *envp[]) |
|||
{ |
|||
char *begin_of_buffer = 0, *end_of_buffer = 0; |
|||
int i; |
|||
|
|||
for (i = 0; i < argc; ++i) { |
|||
if (!begin_of_buffer) |
|||
begin_of_buffer = argv[i]; |
|||
if (!end_of_buffer || end_of_buffer + 1 == argv[i]) |
|||
end_of_buffer = argv[i] + strlen (argv[i]); |
|||
} |
|||
|
|||
for (i = 0; envp[i]; ++i) { |
|||
if (!begin_of_buffer) |
|||
begin_of_buffer = envp[i]; |
|||
if (!end_of_buffer || end_of_buffer + 1 == envp[i]) |
|||
end_of_buffer = envp[i] + strlen(envp[i]); |
|||
} |
|||
|
|||
if (!end_of_buffer) |
|||
return 0; |
|||
|
|||
char **new_environ = malloc ((i + 1) * sizeof (envp[0])); |
|||
|
|||
if (!new_environ) |
|||
return 0; |
|||
|
|||
for (i = 0; envp[i]; ++i) { |
|||
if (!(new_environ[i] = strdup (envp[i]))) |
|||
goto cleanup_enomem; |
|||
} |
|||
new_environ[i] = 0; |
|||
|
|||
if (program_invocation_name) { |
|||
title_progname_full = strdup (program_invocation_name); |
|||
|
|||
if (!title_progname_full) |
|||
goto cleanup_enomem; |
|||
|
|||
char *p = strrchr (title_progname_full, '/'); |
|||
|
|||
if (p) |
|||
title_progname = p + 1; |
|||
else |
|||
title_progname = title_progname_full; |
|||
|
|||
program_invocation_name = title_progname_full; |
|||
program_invocation_short_name = title_progname; |
|||
} |
|||
|
|||
environ = new_environ; |
|||
title_buffer = begin_of_buffer; |
|||
title_buffer_size = end_of_buffer - begin_of_buffer; |
|||
|
|||
return 0; |
|||
|
|||
cleanup_enomem: |
|||
for (--i; i >= 0; --i) { |
|||
free(new_environ[i]); |
|||
} |
|||
free(new_environ); |
|||
return 0; |
|||
} |
|||
#endif |
|||
|
|||
#ifndef HAVE_PIDFILE |
|||
extern char * __progname; |
|||
static int _pidfile_remove(struct pidfh *pfh, int freeit); |
|||
|
|||
static int |
|||
pidfile_verify(struct pidfh *pfh) |
|||
{ |
|||
struct stat sb; |
|||
|
|||
if (pfh == NULL || pfh->pf_fd == -1) |
|||
return (-1); |
|||
/* |
|||
* Check remembered descriptor. |
|||
*/ |
|||
if (fstat(pfh->pf_fd, &sb) == -1) |
|||
return (errno); |
|||
if (sb.st_dev != pfh->pf_dev || sb.st_ino != pfh->pf_ino) |
|||
return (-1); |
|||
return (0); |
|||
} |
|||
|
|||
static int |
|||
pidfile_read(const char *path, pid_t *pidptr) |
|||
{ |
|||
char buf[16], *endptr; |
|||
int error, fd, i; |
|||
|
|||
fd = open(path, O_RDONLY); |
|||
if (fd == -1) |
|||
return (errno); |
|||
|
|||
i = read(fd, buf, sizeof(buf) - 1); |
|||
error = errno; /* Remember errno in case close() wants to change it. */ |
|||
close(fd); |
|||
if (i == -1) |
|||
return (error); |
|||
else if (i == 0) |
|||
return (EAGAIN); |
|||
buf[i] = '\0'; |
|||
|
|||
*pidptr = strtol(buf, &endptr, 10); |
|||
if (endptr != &buf[i]) |
|||
return (EINVAL); |
|||
|
|||
return (0); |
|||
} |
|||
|
|||
struct pidfh * |
|||
pidfile_open(const char *path, mode_t mode, pid_t *pidptr) |
|||
{ |
|||
struct pidfh *pfh; |
|||
struct stat sb; |
|||
int error, fd, len, count; |
|||
struct timespec rqtp; |
|||
|
|||
pfh = malloc(sizeof(*pfh)); |
|||
if (pfh == NULL) |
|||
return (NULL); |
|||
|
|||
if (path == NULL) |
|||
len = snprintf(pfh->pf_path, sizeof(pfh->pf_path), |
|||
"/var/run/%s.pid", __progname); |
|||
else |
|||
len = snprintf(pfh->pf_path, sizeof(pfh->pf_path), |
|||
"%s", path); |
|||
if (len >= (int)sizeof(pfh->pf_path)) { |
|||
free(pfh); |
|||
errno = ENAMETOOLONG; |
|||
return (NULL); |
|||
} |
|||
|
|||
/* |
|||
* Open the PID file and obtain exclusive lock. |
|||
* We truncate PID file here only to remove old PID immediatelly, |
|||
* PID file will be truncated again in pidfile_write(), so |
|||
* pidfile_write() can be called multiple times. |
|||
*/ |
|||
fd = open(pfh->pf_path, |
|||
O_WRONLY | O_CREAT | O_TRUNC | O_NONBLOCK, mode); |
|||
flock (fd, LOCK_EX | LOCK_NB); |
|||
if (fd == -1) { |
|||
count = 0; |
|||
rqtp.tv_sec = 0; |
|||
rqtp.tv_nsec = 5000000; |
|||
if (errno == EWOULDBLOCK && pidptr != NULL) { |
|||
again: |
|||
errno = pidfile_read(pfh->pf_path, pidptr); |
|||
if (errno == 0) |
|||
errno = EEXIST; |
|||
else if (errno == EAGAIN) { |
|||
if (++count <= 3) { |
|||
nanosleep(&rqtp, 0); |
|||
goto again; |
|||
} |
|||
} |
|||
} |
|||
free(pfh); |
|||
return (NULL); |
|||
} |
|||
/* |
|||
* Remember file information, so in pidfile_write() we are sure we write |
|||
* to the proper descriptor. |
|||
*/ |
|||
if (fstat(fd, &sb) == -1) { |
|||
error = errno; |
|||
unlink(pfh->pf_path); |
|||
close(fd); |
|||
free(pfh); |
|||
errno = error; |
|||
return (NULL); |
|||
} |
|||
|
|||
pfh->pf_fd = fd; |
|||
pfh->pf_dev = sb.st_dev; |
|||
pfh->pf_ino = sb.st_ino; |
|||
|
|||
return (pfh); |
|||
} |
|||
|
|||
int |
|||
pidfile_write(struct pidfh *pfh) |
|||
{ |
|||
char pidstr[16]; |
|||
int error, fd; |
|||
|
|||
/* |
|||
* Check remembered descriptor, so we don't overwrite some other |
|||
* file if pidfile was closed and descriptor reused. |
|||
*/ |
|||
errno = pidfile_verify(pfh); |
|||
if (errno != 0) { |
|||
/* |
|||
* Don't close descriptor, because we are not sure if it's ours. |
|||
*/ |
|||
return (-1); |
|||
} |
|||
fd = pfh->pf_fd; |
|||
|
|||
/* |
|||
* Truncate PID file, so multiple calls of pidfile_write() are allowed. |
|||
*/ |
|||
if (ftruncate(fd, 0) == -1) { |
|||
error = errno; |
|||
_pidfile_remove(pfh, 0); |
|||
errno = error; |
|||
return (-1); |
|||
} |
|||
|
|||
snprintf(pidstr, sizeof(pidstr), "%u", getpid()); |
|||
if (pwrite(fd, pidstr, strlen(pidstr), 0) != (ssize_t)strlen(pidstr)) { |
|||
error = errno; |
|||
_pidfile_remove(pfh, 0); |
|||
errno = error; |
|||
return (-1); |
|||
} |
|||
|
|||
return (0); |
|||
} |
|||
|
|||
int |
|||
pidfile_close(struct pidfh *pfh) |
|||
{ |
|||
int error; |
|||
|
|||
error = pidfile_verify(pfh); |
|||
if (error != 0) { |
|||
errno = error; |
|||
return (-1); |
|||
} |
|||
|
|||
if (close(pfh->pf_fd) == -1) |
|||
error = errno; |
|||
free(pfh); |
|||
if (error != 0) { |
|||
errno = error; |
|||
return (-1); |
|||
} |
|||
return (0); |
|||
} |
|||
|
|||
static int |
|||
_pidfile_remove(struct pidfh *pfh, int freeit) |
|||
{ |
|||
int error; |
|||
|
|||
error = pidfile_verify(pfh); |
|||
if (error != 0) { |
|||
errno = error; |
|||
return (-1); |
|||
} |
|||
|
|||
if (unlink(pfh->pf_path) == -1) |
|||
error = errno; |
|||
if (flock(pfh->pf_fd, LOCK_UN) == -1) { |
|||
if (error == 0) |
|||
error = errno; |
|||
} |
|||
if (close(pfh->pf_fd) == -1) { |
|||
if (error == 0) |
|||
error = errno; |
|||
} |
|||
if (freeit) |
|||
free(pfh); |
|||
else |
|||
pfh->pf_fd = -1; |
|||
if (error != 0) { |
|||
errno = error; |
|||
return (-1); |
|||
} |
|||
return (0); |
|||
} |
|||
|
|||
int |
|||
pidfile_remove(struct pidfh *pfh) |
|||
{ |
|||
|
|||
return (_pidfile_remove(pfh, 1)); |
|||
} |
|||
#endif |
|||
@ -0,0 +1,53 @@ |
|||
#ifndef UTIL_H |
|||
#define UTIL_H |
|||
|
|||
#include <sys/types.h> |
|||
#include <sys/types.h> |
|||
#include <sys/socket.h> |
|||
#include <sys/queue.h> |
|||
#include <sys/time.h> |
|||
|
|||
#include <sys/un.h> |
|||
#include <netinet/in.h> |
|||
#include <arpa/inet.h> |
|||
|
|||
#include <signal.h> |
|||
|
|||
#include "main.h" |
|||
|
|||
struct config_file; |
|||
|
|||
/* Create socket and bind it to specified address and port */ |
|||
int make_socket(const char *, u_short ); |
|||
/* Create and bind unix socket */ |
|||
int make_unix_socket (const char *, struct sockaddr_un *); |
|||
/* Parse command line arguments using getopt (3) */ |
|||
void read_cmd_line (int , char **, struct config_file *); |
|||
/* Write pid to file */ |
|||
int write_pid (struct rspamd_main *); |
|||
/* Make specified socket non-blocking */ |
|||
int event_make_socket_nonblocking(int); |
|||
/* Init signals */ |
|||
void init_signals (struct sigaction *, sig_t); |
|||
/* Send specified signal to each worker */ |
|||
void pass_signal_worker (struct workq *, int ); |
|||
|
|||
#ifndef HAVE_SETPROCTITLE |
|||
int init_title(int argc, char *argv[], char *envp[]); |
|||
int setproctitle(const char *fmt, ...); |
|||
#endif |
|||
|
|||
#ifndef HAVE_PIDFILE |
|||
struct pidfh { |
|||
int pf_fd; |
|||
char pf_path[MAXPATHLEN + 1]; |
|||
__dev_t pf_dev; |
|||
ino_t pf_ino; |
|||
}; |
|||
struct pidfh *pidfile_open(const char *path, mode_t mode, pid_t *pidptr); |
|||
int pidfile_write(struct pidfh *pfh); |
|||
int pidfile_close(struct pidfh *pfh); |
|||
int pidfile_remove(struct pidfh *pfh); |
|||
#endif |
|||
|
|||
#endif |
|||
@ -0,0 +1,52 @@ |
|||
|
|||
#include <sys/stat.h> |
|||
#include <sys/param.h> |
|||
#include <sys/types.h> |
|||
#include <unistd.h> |
|||
#include <stdio.h> |
|||
#include <stdlib.h> |
|||
#include <string.h> |
|||
#include <time.h> |
|||
#include <errno.h> |
|||
#include <signal.h> |
|||
|
|||
#include <netinet/in.h> |
|||
#include <syslog.h> |
|||
#include <fcntl.h> |
|||
#include <netdb.h> |
|||
|
|||
#include "util.h" |
|||
#include "main.h" |
|||
#include "upstream.h" |
|||
#include "cfg_file.h" |
|||
|
|||
static |
|||
void sig_handler (int signo) |
|||
{ |
|||
switch (signo) { |
|||
case SIGINT: |
|||
case SIGTERM: |
|||
_exit (1); |
|||
break; |
|||
} |
|||
} |
|||
|
|||
void |
|||
start_worker (struct rspamd_worker *worker, int listen_sock) |
|||
{ |
|||
struct sigaction signals; |
|||
struct config_file *cfg = worker->srv->cfg; |
|||
worker->srv->pid = getpid (); |
|||
worker->srv->type = TYPE_WORKER; |
|||
|
|||
init_signals (&signals, sig_handler); |
|||
sigprocmask (SIG_UNBLOCK, &signals.sa_mask, NULL); |
|||
|
|||
/* Send SIGUSR2 to parent */ |
|||
kill (getppid (), SIGUSR2); |
|||
|
|||
} |
|||
|
|||
/* |
|||
* vi:ts=4 |
|||
*/ |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue