/*
* FreeWnn is a network-extensible Kana-to-Kanji conversion system.
* This file is part of FreeWnn.
*
* Copyright Kyoto University Research Institute for Mathematical Sciences
* 1987, 1988, 1989, 1990, 1991, 1992
* Copyright OMRON Corporation. 1987, 1988, 1989, 1990, 1991, 1992, 1999
* Copyright ASTEC, Inc. 1987, 1988, 1989, 1990, 1991, 1992
* Copyright FreeWnn Project 1999, 2000, 2001, 2002, 2003
*
* Maintainer: FreeWnn Project <freewnn@tomo.gr.jp>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
Jserver (Nihongo Daemon)
*/
static char rcs_id[] = "$Id: de.c,v 1.36 2004/06/18 16:32:41 hiroo Exp $";
#if defined(HAVE_CONFIG_H)
# include <config.h>
#endif
#include "getopt.h"
#include <stdio.h>
#include <ctype.h>
#include <errno.h>
#include <signal.h>
#if STDC_HEADERS
# include <stdlib.h>
# include <string.h>
#else
# if HAVE_MALLOC_H
# include <malloc.h>
# endif
# if HAVE_STRINGS_H
# include <strings.h>
# endif
#endif /* STDC_HEADERS */
#include <sys/ioctl.h>
#include <sys/stat.h>
#if TIME_WITH_SYS_TIME
# include <sys/time.h>
# include <time.h>
#else
# if HAVE_SYS_TIME_H
# include <sys/time.h>
# else
# include <time.h>
# endif /* HAVE_SYS_TIME_H */
#endif /* TIME_WITH_SYS_TIME */
#if HAVE_UNISTD_H
# include <sys/types.h>
# include <unistd.h>
#endif
#ifdef HAVE_FCNTL_H
# include <fcntl.h>
#endif
#if HAVE_SYS_PARAM_H
# include <sys/param.h>
#endif
#include "getopt.h"
#include "commonhd.h"
#include "wnn_config.h"
#include "jd_sock.h"
#include "demcom.h"
#include "wnn_os.h"
#define GLOBAL_VALUE_DEFINE 1
#include "de_header.h"
#undef GLOBAL_VALUE_DEFINE
#include "msg.h"
#ifdef SOLARIS
#ifdef SO_DONTLINGER
#undef SO_DONTLINGER
#endif
#endif /* SOLARIS */
#if defined(HAVE_SETSOCKOPT) && defined(SOL_SOCKET) && defined(SO_NONBLOCK)
#define USE_SETSOCKOPT 1
#else
#undef USE_SETSOCKOPT
#endif
#ifndef min
#define min(x,y) ( (x)<(y) ? (x) : (y) )
#endif
#ifndef INET6
# define OPTIONARGS "Df:s:h:N:p:vu4"
#else
# define OPTIONARGS "Df:s:h:N:p:vu46"
#endif /* INET6 */
/* Accept Socket */
#ifdef INET6
#define MAX_ACCEPTS 3
#else
#define MAX_ACCEPTS 2
#endif
#define UNIX_ACPT 0
#define INET_ACPT 1
#ifdef INET6
#define INET6_ACPT 2
#endif
#define PROTO_ALL 0x1
#define PROTO_UN 0x2
#define PROTO_INET 0x4
#ifdef INET6
#define PROTO_INET6 0x8
#endif
static int listen_proto = PROTO_ALL;
jmp_buf client_dead;
static int port;
static int serverNO = 0;
struct cmblk
{
int sd; /** ソケットのfd **/
};
#define COMS_BLOCK struct cmblk
static COMS_BLOCK *cblk;
/** クライアントごとのソケットを管理するテーブル **/
static COMS_BLOCK accept_blk[MAX_ACCEPTS]; /*accept sock blocks */
/* Communication Buffers */
static char snd_buf[R_BUF_SIZ]; /** 送信バッファ **/
static int sbp; /** 送信バッファポインタ **/
static int rbc; /** 受信バッファcounter **/
static char rcv_buf[S_BUF_SIZ]; /** 受信バッファ **/
#if defined(EAGAIN)
# if defined(EWOULDBLOCK)
# define ERRNO_CHECK(no) ((no) == EAGAIN || (no) == EWOULDBLOCK)
# else /* defined(EWOULDBLOCK) */
# define ERRNO_CHECK(no) ((no) == EAGAIN)
# endif /* defined(EWOULDBLOCK) */
#else /* defined(EAGAIN) */
# if defined(EWOULDBLOCK)
# define ERRNO_CHECK(no) ((no) == EWOULDBLOCK)
# else /* defined(EWOULDBLOCK) */
# define ERRNO_CHECK(no) (0)
# endif /* defined(EWOULDBLOCK) */
#endif /* defined(EAGAIN) */
/* Client Table */
int clientp; /** cblkの有効なデータの最後を差している **/
int cur_clp; /** 現在のクライアントの番号 **/
static fd_set *all_socks; /** ビットパターン
which jserver to select を保持 **/
static fd_set *ready_socks; /** データのきているソケットの
ビットパターンを保持 **/
static fd_set *dummy1_socks, *dummy2_socks;
static int no_of_ready_socks;
static int nofile; /** No. of files **/
struct msg_cat *wnn_msg_cat;
struct msg_cat *js_msg_cat;
/* function prototypes */
static void daemon_main (void);
static void socket_disc_init (void);
static void sel_all (void);
static int get_client (void);
static void new_client (void);
static void daemon_init (void);
static void daemon_fin_un (int);
static void daemon_fin_in (int);
static int rcv_1_client (int);
static void snd_1_client (int, int);
static void socket_init_un (void);
static void socket_init_in (void);
static int socket_accept_un (void);
static int socket_accept_in (int);
static void xerror (char*);
static void get_options (int, char **);
static void usage (void);
static void print_version (void);
#ifdef DEBUG
static void dmp (char*, int);
#endif
static char cmd_name[16];
#if defined(HAVE_LIBWRAP)
int allow_severity;
int deny_severity;
# include <syslog.h>
# include <tcpd.h>
#endif /* HAVE_LIBWRAP */
/* No arguments are used. Only options. */
int
main (int argc, char *argv[])
{
int tmpttyfd;
char *cswidth_name;
extern char *get_cswidth_name ();
extern void set_cswidth ();
char nlspath[64];
strcpy (cmd_name, WNN_DAEMON_NAME);
strcpy (lang_dir, LANG_NAME);
strcpy (nlspath, LIBDIR);
strcat (nlspath, "/%L/%N");
js_msg_cat = msg_open (MESSAGE_FILE, nlspath, lang_dir);
wnn_msg_cat = msg_open ("libwnn.msg", nlspath, lang_dir);
if (wnn_msg_cat == NULL)
{
log_err ("cannot open message file libwnn.msg.");
}
if (cswidth_name = get_cswidth_name (LANG_NAME))
set_cswidth (create_cswidth (cswidth_name));
port = -1;
/* option default */
option_flag = (OPT_FORK & ~OPT_VERBOSE);
setuid (geteuid ());
get_options (argc, argv);
print_version();
log_debug("invoked as %s.", argv[0]);
if (option_flag & OPT_FORK)
{
if (fork ())
{
signal (SIGCHLD, _exit);
signal (SIGHUP, SIG_IGN);
signal (SIGINT, SIG_IGN);
signal (SIGQUIT, SIG_IGN);
#ifdef SIGTSTP
signal (SIGTSTP, SIG_IGN);
#endif
signal (SIGTERM, _exit);
pause ();
}
}
#if defined(HAVE_LIBWRAP)
allow_severity = LOG_INFO;
deny_severity = LOG_WARNING;
/* hosts_access_verbose = 2; */
#endif /* HAVE_LIBWRAP */
signal (SIGHUP, signal_hand);
signal (SIGINT, signal_hand);
signal (SIGQUIT, signal_hand);
signal (SIGTERM, terminate_hand);
if (option_flag & OPT_FORK)
{
#ifdef SIGTSTP
signal (SIGTSTP, SIG_IGN);
#endif /* SIGTSTP */
}
read_default ();
daemon_init ();
env_init ();
if (file_init () == NULL)
{
exit (1);
}
dic_init ();
if (NULL == get_kaiseki_area (LENGTHCONV + 1)) /* 変換可能文字数 */
{
log_err ("get_kaiseki_area failed.");
exit (1);
}
init_work_areas ();
init_jmt ();
read_default_files ();
if (option_flag & OPT_FORK)
{
/* End of initialization, kill parent */
kill (getppid (), SIGTERM);
fclose (stdin);
fclose (stdout);
if (!(option_flag & OPT_VERBOSE))
{
#if !(defined(BSD) && (BSD >= 199306)) /* !4.4BSD-Lite by Taoka */
fclose (stderr);
#else /* 4.4BSD-Lite */
int fd = open ("/dev/null", O_WRONLY);
if (fd < 0)
{
xerror ("Cannot open /dev/null");
}
dup2 (fd, 2);
close (fd);
#endif /* 4.4BSD-Lite */
}
#ifdef SETPGRP_VOID
setpgrp ();
#else /* !SETPGRP_VOID */
# if !defined(TIOCNOTTY) && defined(SVR4)
# define TIOCNOTTY _IO('t', 113)
# endif /* !defined(TIOCNOTTY) && defined(SVR4) */
#ifndef HITACHI
if ((tmpttyfd = open ("/dev/tty", O_RDWR)) >= 0)
{
ioctl (tmpttyfd, TIOCNOTTY, 0);
close (tmpttyfd);
}
#endif /* HITACHI */
#endif /* SETPGRP_VOID */
}
daemon_main ();
daemon_fin ();
return (0); /* it is not reached. only for avoiding compiler warning. */
}
static void
daemon_main (void)
{
for (;;)
{
c_c = NULL; /* Added for logging: server section */
sel_all ();
new_client ();
for (;;)
{
if (get_client () == -1)
break;
c_c = &client[cur_clp];
rbc = 0;
sbp = 0;
/* if(rcv_1_client(cur_clp) == 0){ del_client(); continue; } */
if (setjmp (client_dead))
{
del_client ();
continue;
}
do_command (c_c);
}
}
}
/*
allocs area for selecting socketts
*/
static void
socket_disc_init (void)
{
if (WNN_NFD <= FD_SETSIZE)
{
nofile = WNN_NFD;
}
else
{
nofile = FD_SETSIZE;
}
all_socks = (fd_set *) malloc (sizeof (fd_set));
FD_ZERO (all_socks);
ready_socks = (fd_set *) malloc (sizeof (fd_set));
dummy1_socks = (fd_set *) malloc (sizeof (fd_set));
dummy2_socks = (fd_set *) malloc (sizeof (fd_set));
}
/** 全てのソケットについて待つ **/
static void
sel_all (void)
{
memcpy (ready_socks, all_socks, sizeof (fd_set));
bzero (dummy1_socks, sizeof (fd_set));
bzero (dummy2_socks, sizeof (fd_set));
top:
errno = 0;
if ((no_of_ready_socks = select (nofile, ready_socks, dummy1_socks, dummy2_socks, NULL)) == -1)
{
if (errno == EINTR)
goto top;
xerror ("select error");
}
#ifdef DEBUG
log_debug ("select OK, ready_socks[0]=%02X, n-r-s=%x\n", ready_socks[0], no_of_ready_socks);
#endif
}
/** ready_socksから今やる一人を取り出して返す(cur_clpにもセットする)
誰も居ない時は-1
スケジューリングはラウンドロビン **/
static int
get_client (void)
{
int i;
if (no_of_ready_socks == 0)
return -1; /* no client waits service */
for (i = cur_clp;;)
{
if (no_of_ready_socks == 0)
return -1;
i++;
if (i >= clientp)
i = 0;
if (FD_ISSET (cblk[i].sd, ready_socks))
{
FD_CLR (cblk[i].sd, ready_socks);
no_of_ready_socks--;
return cur_clp = i;
}
}
}
/** 新しいクライアントが居るか否かを調べる
居た場合はcblkに登録する **/
static void
new_client (void) /* NewClient */
{
int sd;
int full, i;
FILE *f[3];
char gomi[1024];
#ifdef HAVE_LIBWRAP
int is_internet_socket;
struct request_info tcpd_request;
#endif /* HAVE_LIBWRAP */
#ifdef AF_UNIX
if ((serverNO == 0) &&
(FD_ISSET (accept_blk[UNIX_ACPT].sd, ready_socks)))
{
FD_CLR (accept_blk[UNIX_ACPT].sd, ready_socks);
no_of_ready_socks--;
sd = socket_accept_un ();
#ifdef HAVE_LIBWRAP
is_internet_socket = 0;
#endif
}
else
#endif
#ifdef INET6
if (FD_ISSET (accept_blk[INET6_ACPT].sd, ready_socks))
{
FD_CLR (accept_blk[INET6_ACPT].sd, ready_socks);
no_of_ready_socks--;
sd = socket_accept_in (accept_blk[INET6_ACPT].sd);
#ifdef HAVE_LIBWRAP
is_internet_socket = 1;
#endif
}
else
#endif
if (FD_ISSET (accept_blk[INET_ACPT].sd, ready_socks))
{
FD_CLR (accept_blk[INET_ACPT].sd, ready_socks);
no_of_ready_socks--;
sd = socket_accept_in (accept_blk[INET_ACPT].sd);
#ifdef HAVE_LIBWRAP
is_internet_socket = 1;
#endif
}
else
{
return;
}
log_debug ("new client: sd = %d", sd);
/* reserve 2 fd */
for (full = i = 0; i < 2; i++)
{
if (NULL == (f[i] = fopen ("/dev/null", "r")))
{
full = 1;
}
}
for (i = 0; i < 2; i++)
{
if (NULL != f[i])
fclose (f[i]);
}
if (full || sd >= nofile || clientp >= max_client)
{
log_err ("no more client.");
#ifdef HAVE_RECV
recv (sd, gomi, 1024, 0);
#else
read (sd, gomi, 1024);
#endif
shutdown (sd, 2);
#ifdef HAVE_CLOSESOCKET
closesocket (sd);
#else
close (sd);
#endif
return;
}
#ifdef HAVE_LIBWRAP
if (is_internet_socket) {
request_init (&tcpd_request,RQ_DAEMON, WNN_DAEMON_NAME,
RQ_FILE, sd, NULL);
fromhost (&tcpd_request);
if (!hosts_access (&tcpd_request))
{
log_err ("reject client."); /* should be log_info? */
/* should we log IP address / hostname? */
#ifdef HAVE_RECV
recv (sd, gomi, 1024, 0);
#else
read (sd, gomi, 1024);
#endif
shutdown (sd, 2);
#ifdef HAVE_CLOSESOCKET
closesocket (sd);
#else
close (sd);
#endif
return;
}
}
#endif /* HAVE_LIBWRAP */
cblk[clientp].sd = sd;
FD_SET (sd, all_socks);
for (i = 0; i < WNN_MAX_ENV_OF_A_CLIENT; i++)
{
(client[clientp].env)[i] = -1;
}
clientp++;
}
/** クライアントをcblkから削除する **/
/* delete Client (please call from JS_CLOSE) */
void
del_client (void)
{
disconnect_all_env_of_client ();
FD_CLR (cblk[cur_clp].sd, all_socks);
#ifdef HAVE_CLOSESOCKET
closesocket (cblk[cur_clp].sd);
#else
close (cblk[cur_clp].sd);
#endif
/* logging here because c_c (used in log_debug) will be broken after
following section */
log_debug("Delete Client: cur_clp = %d\n", cur_clp);
cblk[cur_clp] = cblk[clientp - 1];
client[cur_clp] = client[clientp - 1];
/* Clear host/user name with zero - needed for logging */
client[clientp - 1].user_name[0] = '\0'; /* Should we use bzero()? */
client[clientp - 1].host_name[0] = '\0';
clientp--;
}
/** サーバをイニシャライズする **/
static void
daemon_init (void) /* initialize Daemon */
{
/*
signal (SIGHUP, SIG_IGN);
signal (SIGINT, SIG_IGN);
signal (SIGQUIT, SIG_IGN);
*/
if ((cblk = (COMS_BLOCK *) malloc (max_client * sizeof (COMS_BLOCK))) == NULL)
{
xerror ("daemon_init: ");
}
if ((client = (CLIENT *) malloc (max_client * sizeof (CLIENT))) == NULL)
{
xerror ("daemon_init: ");
}
SDRAND (time (NULL));
clientp = 0; /* V3.0 */
cur_clp = 0; /* V3.0 */
socket_disc_init ();
#ifdef INET6
if (listen_proto&(PROTO_ALL|PROTO_INET|PROTO_INET6))
socket_init_in ();
#else
if (listen_proto&(PROTO_ALL|PROTO_INET))
socket_init_in ();
#endif
#ifdef AF_UNIX
if (listen_proto&(PROTO_ALL|PROTO_UN))
socket_init_un ();
#endif /* AF_UNIX */
}
/** サーバを終わる **/
#ifdef AF_UNIX
static void
daemon_fin_un (int sock_d_un)
{
int trueFlag = 1;
struct sockaddr_un addr_un;
socklen_t addrlen;
if (serverNO == 0)
{
#ifndef SOLARIS
#if defined(FIONBIO)
ioctl (sock_d_un, FIONBIO, &trueFlag);
#endif
#else /* !SOLARIS */
fcntl (sock_d_un, F_SETFL, F_UNLCK);
#endif /* !SOLARIS */
for (;;)
{
addrlen = sizeof (addr_un);
if (accept (sock_d_un, (struct sockaddr *) &addr_un, &addrlen) < 0)
break;
/* EWOULDBLOCK EXPECTED, but we don't check */
}
shutdown (sock_d_un, 2);
close (sock_d_un);
}
}
#endif /* AF_UNIX */
static void
daemon_fin_in (int sock_d_in)
{
int trueFlag = 1;
struct sockaddr_in addr_in;
socklen_t addrlen;
#ifdef USE_SETSOCKOPT
int on = ~0;
#endif
#ifndef SOLARIS
#ifdef USE_SETSOCKOPT
setsockopt (sock_d_in, SOL_SOCKET, SO_NONBLOCK, &on, sizeof (int));
#else
#if defined(FIONBIO)
ioctl (sock_d_in, FIONBIO, &trueFlag);
#endif
#endif /* USE_SETSOCKOPT */
#else /* !SOLARIS */
fcntl (sock_d_in, F_SETFL, F_UNLCK);
#endif /* !SOLARIS */
for (;;)
{
addrlen = sizeof (addr_in);
if (accept (sock_d_in, (struct sockaddr *) &addr_in, &addrlen) < 0)
break;
/* EWOULDBLOCK EXPECTED, but we don't check */
}
shutdown (sock_d_in, 2);
#ifdef HAVE_CLOSESOCKET
closesocket (sock_d_in);
#else
close (sock_d_in);
#endif
}
void
daemon_fin (void)
{
int fd;
#ifdef AF_UNIX
int sock_d_un = accept_blk[UNIX_ACPT].sd;
#endif /* AF_UNIX */
int sock_d_in = accept_blk[INET_ACPT].sd;
#ifdef INET6
int sock_d_in6 = accept_blk[INET6_ACPT].sd;
#endif
/*
accept all pending connection from new clients,
avoiding kernel hangup.
*/
#ifdef AF_UNIX
daemon_fin_un (sock_d_un);
#endif
daemon_fin_in (sock_d_in);
#ifdef INET6
daemon_fin_in (sock_d_in6);
#endif
for (fd = nofile - 1; fd >= 0; fd--)
{
if ((fd != sock_d_in) &&
#ifdef INET6
(fd != sock_d_in6) &&
#endif
#ifdef AF_UNIX
(fd != sock_d_un) &&
#endif /* AF_UNIX */
FD_ISSET (fd, all_socks))
{
shutdown (fd, 2);
#ifdef HAVE_CLOSESOCKET
closesocket (fd);
#else
close (fd);
#endif
}
}
}
/*------*/
/** **/
char *
gets_cur (char *buffer, size_t buffer_size)
{
char *b;
if (!buffer || !buffer_size)
return NULL;
b = buffer;
while (--buffer_size && (*b = getc_cur ()) != '\0')
b++;
if (!buffer_size)
{
*b = '\0';
while (getc_cur () != '\0')
;
}
return buffer;
}
/** **/
w_char *
getws_cur (w_char *buffer, size_t buffer_size)
{
w_char *b;
if (!buffer || !buffer_size)
return NULL;
b = buffer;
while (--buffer_size && (*b = get2_cur ()) != 0)
b++;
if (!buffer_size)
{
*b = 0;
while (getc_cur () != 0)
;
}
return buffer;
}
/** カレント・クライアントから2バイト取る **/
int
get2_cur (void)
{
int x;
x = getc_cur ();
return (x << 8) | getc_cur ();
}
/** カレント・クライアントから4バイト取る **/
int
get4_cur (void)
{
int x1, x2, x3;
x1 = getc_cur ();
x2 = getc_cur ();
x3 = getc_cur ();
return (x1 << (8 * 3)) | (x2 << (8 * 2)) | (x3 << (8 * 1)) | getc_cur ();
}
/** カレント・クライアントから1バイト取る **/
int
getc_cur (void)
{
static int rbp;
if (rbc <= 0)
{
rbc = rcv_1_client (cur_clp);
rbp = 0;
}
rbc--;
return rcv_buf[rbp++] & 0xFF;
}
/** クライアントから1パケット取る **/
static int
rcv_1_client (int clp) /* clp=クライアント番号 */
{
int cc = 0;
while (cc <= 0)
{
errno = 0;
#ifdef HAVE_RECV
cc = recv (cblk[clp].sd, rcv_buf, S_BUF_SIZ, 0);
#else
cc = read (cblk[clp].sd, rcv_buf, S_BUF_SIZ);
#endif
if (cc <= 0)
{
if (ERRNO_CHECK (errno))
{
continue;
}
else if (cc == 0)
{ /* client dead */
longjmp (client_dead, 666);
}
else
{ /* cc == -1 */
if (errno != EINTR)
longjmp (client_dead, 666);
continue;
}
}
}
#ifdef DEBUG
log_debug ("rcv: clp = %d, sd = %d, cc = %d", clp, cblk[clp].sd, cc);
dmp (rcv_buf, cc);
#endif
return cc;
}
/** クライアントへ1パケット送る **/
static void
snd_1_client (int clp, /* clp: クライアント番号 */
int n /* n : number of bytes to send */ )
{
int cc, x;
#ifdef DEBUG
log_debug ("snd: clp = %d, sd = %d", clp, cblk[clp].sd);
dmp (snd_buf, n);
#endif
for (cc = 0; cc < n;)
{
errno = 0;
#ifdef HAVE_SEND
x = send (cblk[clp].sd, &snd_buf[cc], n - cc, 0);
#else
x = write (cblk[clp].sd, &snd_buf[cc], n - cc);
#endif
if (x < 0)
{
if (ERRNO_CHECK (errno) || errno == EINTR)
{
errno = 0;
continue;
}
else
{ /* client dead */
longjmp (client_dead, 666);
}
}
cc += x;
}
}
/** **/
void
puts_cur (char *p)
{
int c;
while (c = *p++)
putc_cur (c);
putc_cur (0);
}
/** **/
void
puts_n_cur (char *p, int n)
{
int c;
while ((c = *p++) && --n >= 0)
putc_cur (c);
putc_cur (0);
}
/** **/
void
putws_cur (w_char *p)
{
int c;
while (c = *p++)
put2_cur (c);
put2_cur (0);
}
/** **/
void
putnws_cur (w_char *p, int n)
{
int c;
for (; n > 0; n--)
{
if ((c = *p++) == 0)
break;
put2_cur (c);
}
put2_cur (0);
}
/** カレント・クライアントへ2バイト送る **/
void
put2_cur (int c)
{
putc_cur (c >> (8 * 1));
putc_cur (c);
}
/** カレント・クライアントへ4バイト送る **/
void
put4_cur (int c)
{
putc_cur (c >> (8 * 3));
putc_cur (c >> (8 * 2));
putc_cur (c >> (8 * 1));
putc_cur (c);
}
/** カレント・クライアントへ1バイト送る **/
void
putc_cur (int c)
{
snd_buf[sbp++] = c;
if (sbp >= R_BUF_SIZ)
{
snd_1_client (cur_clp, R_BUF_SIZ);
sbp = 0;
}
}
/** カレント・クライアントの送信バッファをフラッシュする **/
void
putc_purge (void)
{
if (sbp != 0)
{
snd_1_client (cur_clp, sbp);
sbp = 0;
}
}
/*-----*/
/** ソケットのイニシャライズ **/
#ifdef AF_UNIX
static void
socket_init_un (void)
{
struct sockaddr_un saddr_un;
int sock_d_un;
if (serverNO == 0)
{
saddr_un.sun_family = AF_UNIX;
unlink (sockname);
strcpy (saddr_un.sun_path, sockname);
if ((sock_d_un = socket (AF_UNIX, SOCK_STREAM, 0)) == ERROR)
{
xerror ("could not create unix domain socket");
}
if (bind (sock_d_un, (struct sockaddr *) &saddr_un, strlen (saddr_un.sun_path) + 2) == ERROR)
{
shutdown (sock_d_un, 2);
xerror ("could not bind unix domain socket");
}
if (listen (sock_d_un, 5) == ERROR)
{
shutdown (sock_d_un, 2);
xerror ("could not listen unix domain socket");
}
chmod (sockname, 0777);
signal (SIGPIPE, SIG_IGN);
#ifdef DEBUG
log_debug ("sock_d_un = %d", sock_d_un);
#endif
accept_blk[UNIX_ACPT].sd = sock_d_un;
FD_SET (sock_d_un, all_socks);
}
}
#endif /* AF_UNIX */
/* Inet V3.0 */
static void
socket_init_in (void)
{
#ifndef SOLARIS
int on = 1;
#else /* SOLARIS */
int on = 0;
#endif /* SOLARIS */
struct servent *sp;
#if !defined(SO_DONTLINGER) && defined(SO_LINGER)
struct linger linger;
#endif
#ifdef INET6
struct addrinfo hints, *res, *res0;
int error;
char sport[6];
#else
struct sockaddr_in saddr_in;
#endif
int sock_d_in;
if (port < 0)
{
if ((sp = getservbyname (SERVERNAME, "tcp")) == NULL)
{
port = WNN_PORT_IN;
}
else
{
port = ntohs (sp->s_port);
}
}
port += serverNO;
#if DEBUG
log_debug ("port=%x", port);
#endif
#ifdef INET6
memset(&hints, 0, sizeof(hints));
if (listen_proto&PROTO_INET && !(listen_proto&PROTO_INET6))
hints.ai_family = PF_INET;
else if (listen_proto&PROTO_INET6 && !(listen_proto&PROTO_INET))
hints.ai_family = PF_INET6;
else
hints.ai_family = PF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
sprintf(sport, "%d", port);
error = getaddrinfo(NULL, sport, &hints, &res0);
if (error)
{
xerror (gai_strerror(error));
}
for (res = res0; res; res = res->ai_next) {
if (res->ai_family == AF_INET || res->ai_family == AF_INET6){
if ((sock_d_in = socket (res->ai_family, res->ai_socktype, res->ai_protocol)) == ERROR)
#else
saddr_in.sin_family = AF_INET;
saddr_in.sin_port = htons (port);
saddr_in.sin_addr.s_addr = htonl (INADDR_ANY);
if ((sock_d_in = socket (AF_INET, SOCK_STREAM, 0)) == ERROR)
#endif
{
#ifdef INET6
if (res->ai_family == AF_INET6)
xerror ("could not create inet6 socket");
else if (res->ai_family == AF_INET)
#endif
xerror ("could not create inet socket");
}
setsockopt (sock_d_in, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof (int));
#ifdef SO_DONTLINGER
setsockopt (sock_d_in, SOL_SOCKET, SO_DONTLINGER, (char *) 0, 0);
#else
# ifdef SO_LINGER
linger.l_onoff = 0;
linger.l_linger = 0;
setsockopt (sock_d_in, SOL_SOCKET, SO_LINGER, (char *) &linger, sizeof linger);
# endif /* SO_LINGER */
#endif /* SO_DONTLINGER */
#ifdef INET6
if (bind (sock_d_in, res->ai_addr, res->ai_addrlen) == ERROR)
#else
if (bind (sock_d_in, (struct sockaddr *) &saddr_in, sizeof (saddr_in)) == ERROR)
#endif
{
shutdown (sock_d_in, 2);
#ifdef INET6
if (res->ai_family == AF_INET6)
xerror ("can't bind inet6 socket");
else if (res->ai_family == AF_INET)
#endif
xerror ("can't bind inet socket");
}
if (listen (sock_d_in, 5) == ERROR)
{
shutdown (sock_d_in, 2);
#ifdef INET6
if (res->ai_family == AF_INET6)
xerror ("can't listen inet6 socket");
else if (res->ai_family == AF_INET)
#endif
xerror ("can't listen inet socket");
}
#if DEBUG
log_debug ("sock_d_in = %d", sock_d_in);
#endif
FD_SET (sock_d_in, all_socks);
#ifdef INET6
if (res->ai_family == AF_INET)
accept_blk[INET_ACPT].sd = sock_d_in;
else if (res->ai_family == AF_INET6)
accept_blk[INET6_ACPT].sd = sock_d_in;
}
}
freeaddrinfo(res0);
#else
accept_blk[INET_ACPT].sd = sock_d_in;
#endif
}
/** accept new client socket **/
#ifdef AF_UNIX
static int
socket_accept_un (void)
{
struct sockaddr_un addr;
socklen_t addrlen;
addrlen = sizeof (addr);
return accept (accept_blk[UNIX_ACPT].sd, (struct sockaddr *) &addr, &addrlen);
}
#endif /* AF_UNIX */
static int
socket_accept_in (int fd)
{
struct sockaddr_in addr;
socklen_t addrlen;
addrlen = sizeof (addr);
return accept (fd, (struct sockaddr *) &addr, &addrlen);
}
static void
xerror (char *s)
{
log_err ("%s (%s).", s, strerror(errno));
exit (1);
}
#if DEBUG
static void
dmp (char *p, int n)
{
int i, j;
for (i = 0; i < n; i += 16)
{
for (j = 0; j < 16; j++)
{
fprintf (stderr, "%02x ", p[i + j] & 0xFF);
}
fprintf (stderr, "n=%d\n", n);
}
}
#endif
static void
get_options (int argc, char **argv)
{
int c;
int digit_optind = 0;
strcpy (jserverrcfile, LIBDIR); /* usr/local/lib/wnn */
strcat (jserverrcfile, SERVER_INIT_FILE); /* ja_JP/jserverrc */
while (1)
{
int this_option_optind = optind ? optind : 1;
int option_index = 0;
static struct option long_options[] =
{
{"baseport", 1, NULL, 'p'},
{"inet", 0, NULL, '4'},
{"inet6", 0, NULL, '6'},
{"jserverrc", 1, NULL, 'f'},
{"version", 0, NULL, 'v'},
{0, 0, 0, 0}
};
c = getopt_long (argc, argv, OPTIONARGS,
long_options, &option_index);
if (c == -1)
break;
switch (c)
{
case 'D': /* do not detach, not a daemon */
option_flag &= ~OPT_FORK;
break;
case 'f': /* --jserverrc FILENAME */
strcpy (jserverrcfile, optarg);
break;
case 's':
/* should nuke noisy someday */
noisy = 1; option_flag |= OPT_VERBOSE;
if (strcmp ("-", optarg) != 0)
{
/** maybe FILE wnnerr = stderr; or wnnerr = open(optarg...) is better? or freopen is normal method? */
/** take a look at daemon(3) */
if (freopen (optarg, "a", stderr) == NULL)
{
/** fprintf to stderr? */
printf ("Error in opening scriptfile %s.\n", optarg);
exit (1);
}
}
log_debug ("script started");
break;
case 'h':
/* var hinsi_file_name polluted */
hinsi_file_name = optarg;
break;
case 'N':
serverNO = atoi (optarg);
/* error handling needed */
break;
case 'p':
port = atoi (optarg);
/* error handling needed */
break;
case 'v':
print_version();
usage();
break;
case 'u':
listen_proto &= ~PROTO_ALL;
listen_proto |= PROTO_UN;
break;
case '4':
listen_proto &= ~PROTO_ALL;
listen_proto |= PROTO_INET;
break;
#ifdef INET6
case '6':
listen_proto &= ~PROTO_ALL;
listen_proto |= PROTO_INET6;
break;
#endif /* INET6 */
default:
print_version();
usage();
break;
}
}
}
/*
*/
void
js_who (void)
{
int i, j;
put4_cur (clientp);
for (i = 0; i < clientp; i++)
{
put4_cur (cblk[i].sd);
puts_cur (client[i].user_name);
puts_cur (client[i].host_name);
for (j = 0; j < WNN_MAX_ENV_OF_A_CLIENT; j++)
{
put4_cur ((client[i].env)[j]);
}
}
putc_purge ();
}
void
js_kill (void)
{
if (clientp == 1)
{
put4_cur (0);
putc_purge ();
terminate_hand ();
}
else
{
put4_cur (clientp - 1);
putc_purge ();
}
}
void
usage (void)
{
fprintf(stderr,
#ifdef INET6
"usage: %s [-Du46][-f <init_file> -s <log_file(\"-\" for stderr)> -h <pos_file> -N <serverNO> -p <port_base>]\n",
#else
"usage: %s [-Du4][-f <init_file> -s <log_file(\"-\" for stderr)> -h <pos_file> -N <serverNO> -p <port_base>]\n",
#endif
cmd_name);
fprintf(stderr,
" %s -v\n",
cmd_name);
exit (1);
}
void
print_version (void)
{
#if defined(CHINESE)
printf ("%s (%s) Chinese Multi Client Server\n", cmd_name, SER_VERSION);
#elif defined(KOREAN)
printf ("%s (%s) Korean Multi Client Server\n", cmd_name, SER_VERSION);
#else
printf ("%s (%s) Nihongo Multi Client Server\n", cmd_name, SER_VERSION);
#endif /* CHINESE */
}
syntax highlighted by Code2HTML, v. 0.9.1