diff --git a/clients/.gitignore b/clients/.gitignore index 9f46af6772..1b3c7d7eff 100644 --- a/clients/.gitignore +++ b/clients/.gitignore @@ -12,3 +12,4 @@ upsmon upsrw upssched .libs +*.exe diff --git a/clients/Makefile.am b/clients/Makefile.am index 7199ec262b..6b6e8909d7 100644 --- a/clients/Makefile.am +++ b/clients/Makefile.am @@ -19,8 +19,14 @@ endif bin_PROGRAMS = upsc upslog upsrw upscmd dist_bin_SCRIPTS = upssched-cmd +if HAVE_WINDOWS_SOCKETS +sbin_PROGRAMS = upsmon upssched message +#TODO : port libnutclient +lib_LTLIBRARIES = libupsclient.la +else !HAVE_WINDOWS_SOCKETS sbin_PROGRAMS = upsmon upssched lib_LTLIBRARIES = libupsclient.la libnutclient.la +endif !HAVE_WINDOWS_SOCKETS if WITH_DEV include_HEADERS = upsclient.h ../include/parseconf.h nutclient.h endif @@ -33,6 +39,9 @@ upscmd_SOURCES = upscmd.c upsclient.h upsrw_SOURCES = upsrw.c upsclient.h upslog_SOURCES = upslog.c upsclient.h upslog.h upsmon_SOURCES = upsmon.c upsmon.h upsclient.h +if HAVE_WINDOWS_SOCKETS +message_SOURCES = message.c +endif upssched_SOURCES = upssched.c upssched.h upssched_LDADD = ../common/libcommon.la ../common/libparseconf.la $(NETLIBS) @@ -45,15 +54,21 @@ upsstats_cgi_SOURCES = upsstats.c upsclient.h status.h upsstats.h \ upsimagearg.h cgilib.c cgilib.h # not LDADD. + +# libupsclient version information +# http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html +if HAVE_WINDOWS_SOCKETS +libupsclient_la_SOURCES = upsclient.c upsclient.h ../common/wincompat.c ../common/common.c +libupsclient_la_LIBADD = ../common/libparseconf.la -lws2_32 +libupsclient_la_LDFLAGS = -version-info 3:1:0 -no-undefined +else !HAVE_WINDOWS_SOCKETS libupsclient_la_SOURCES = upsclient.c upsclient.h libupsclient_la_LIBADD = ../common/libparseconf.la +libupsclient_la_LDFLAGS = -version-info 3:1:0 +endif !HAVE_WINDOWS_SOCKETS + if WITH_SSL libupsclient_la_LIBADD += $(LIBSSL_LIBS) endif -# libupsclient version information -# http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html -libupsclient_la_LDFLAGS = -version-info 3:1:0 - libnutclient_la_SOURCES = nutclient.h nutclient.cpp - diff --git a/clients/message.c b/clients/message.c new file mode 100644 index 0000000000..b4c3598317 --- /dev/null +++ b/clients/message.c @@ -0,0 +1,10 @@ +#ifdef WIN32 +#include + +int main(int args,char ** argv) +{ + MessageBox(NULL,argv[1],"Network UPS Tools",MB_OK|MB_ICONEXCLAMATION|MB_SERVICE_NOTIFICATION); + + return 0; +} +#endif diff --git a/clients/nutclient.cpp b/clients/nutclient.cpp index dcf0c2da75..2abec9db49 100644 --- a/clients/nutclient.cpp +++ b/clients/nutclient.cpp @@ -16,7 +16,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - +#ifndef WIN32 #include "nutclient.h" #include @@ -1723,4 +1723,4 @@ void nutclient_execute_device_command(NUTCLIENT_t client, const char* dev, const } /* extern "C" */ - +#endif diff --git a/clients/upsc.c b/clients/upsc.c index ae584d3550..f048881809 100644 --- a/clients/upsc.c +++ b/clients/upsc.c @@ -21,9 +21,11 @@ #include "common.h" #include "nut_platform.h" +#ifndef WIN32 #include #include #include +#endif #include "upsclient.h" diff --git a/clients/upsclient.c b/clients/upsclient.c index 70b6595336..d83a31663a 100644 --- a/clients/upsclient.c +++ b/clients/upsclient.c @@ -21,21 +21,36 @@ #include "config.h" /* safe because it doesn't contain prototypes */ +#ifndef WIN32 #ifdef HAVE_PTHREAD /* this include is needed on AIX to have errno stored in thread local storage */ #include #endif +#endif #include -#include #include #include #include #include +#ifndef WIN32 +#include #include #include #include #include +#define SOCK_OPT_CAST +#else +#define SOCK_OPT_CAST (char *) +/* Those 2 files for support of getaddrinfo, getnameinfo and freeaddrinfo + on Windows 2000 and older versions */ +#include +#include +/* This override network system calls to adapt to Windows specificity */ +#define W32_NETWORK_CALL_OVERRIDE +#include "wincompat.h" +#undef W32_NETWORK_CALL_OVERRIDE +#endif #include "upsclient.h" #include "common.h" @@ -840,8 +855,16 @@ int upscli_tryconnect(UPSCONN_t *ups, const char *host, int port, int flags,stru fd_set wfds; int error; socklen_t error_size; + +#ifndef WIN32 long fd_flags; +#else + HANDLE event = NULL; + unsigned long argp; + WSADATA WSAdata; + WSAStartup(2,&WSAdata); +#endif if (!ups) { return -1; } @@ -900,7 +923,7 @@ int upscli_tryconnect(UPSCONN_t *ups, const char *host, int port, int flags,stru { case EAFNOSUPPORT: case EINVAL: - break; + break; default: ups->upserror = UPSCLI_ERR_SOCKFAILURE; ups->syserrno = errno; @@ -910,13 +933,30 @@ int upscli_tryconnect(UPSCONN_t *ups, const char *host, int port, int flags,stru /* non blocking connect */ if(timeout != NULL) { +#ifndef WIN32 fd_flags = fcntl(sock_fd, F_GETFL); fd_flags |= O_NONBLOCK; fcntl(sock_fd, F_SETFL, fd_flags); } +#else + event = CreateEvent(NULL, /* Security */ + FALSE, /* auto-reset */ + FALSE, /* initial state */ + NULL); /* no name */ + + /* Associate socket event to the socket via its Event object */ + WSAEventSelect( sock_fd, event, FD_CONNECT ); + CloseHandle(event); + } +#endif + while ((v = connect(sock_fd, ai->ai_addr, ai->ai_addrlen)) < 0) { +#ifndef WIN32 if(errno == EINPROGRESS) { +#else + if(errno == WSAEWOULDBLOCK) { +#endif FD_ZERO(&wfds); FD_SET(sock_fd, &wfds); select(sock_fd+1,NULL,&wfds,NULL, @@ -924,7 +964,7 @@ int upscli_tryconnect(UPSCONN_t *ups, const char *host, int port, int flags,stru if (FD_ISSET(sock_fd, &wfds)) { error_size = sizeof(error); getsockopt(sock_fd,SOL_SOCKET,SO_ERROR, - &error,&error_size); + SOCK_OPT_CAST&error,&error_size); if( error == 0) { /* connect successful */ v = 0; @@ -960,9 +1000,14 @@ int upscli_tryconnect(UPSCONN_t *ups, const char *host, int port, int flags,stru /* switch back to blocking operation */ if(timeout != NULL) { +#ifndef WIN32 fd_flags = fcntl(sock_fd, F_GETFL); fd_flags &= ~O_NONBLOCK; fcntl(sock_fd, F_SETFL, fd_flags); +#else + argp = 0; + ioctlsocket(sock_fd,FIONBIO,&argp); +#endif } ups->fd = sock_fd; diff --git a/clients/upscmd.c b/clients/upscmd.c index f4990a872c..f00e740558 100644 --- a/clients/upscmd.c +++ b/clients/upscmd.c @@ -20,11 +20,15 @@ #include "common.h" #include "nut_platform.h" +#ifndef WIN32 #include #include #include #include #include +#else +#include "wincompat.h" +#endif #include "upsclient.h" diff --git a/clients/upslog.c b/clients/upslog.c index 7c534c7f72..b6b169d138 100644 --- a/clients/upslog.c +++ b/clients/upslog.c @@ -39,13 +39,19 @@ #include "timehead.h" #include "upslog.h" +#ifdef WIN32 +#include "wincompat.h" +#endif + static int port, reopen_flag = 0, exit_flag = 0; static char *upsname, *hostname; static UPSCONN_t ups; static FILE *logfile; static const char *logfn, *monhost; +#ifndef WIN32 static sigset_t nut_upslog_sigmask; +#endif static char logbuffer[LARGEBUF], *logformat; static flist_t *fhead = NULL; @@ -67,6 +73,7 @@ static void reopen_log(void) fatal_with_errno(EXIT_FAILURE, "could not reopen logfile %s", logfn); } +#ifndef WIN32 static void set_reopen_flag(int sig) { reopen_flag = sig; @@ -76,10 +83,12 @@ static void set_exit_flag(int sig) { exit_flag = sig; } +#endif /* handlers: reload on HUP, exit on INT/QUIT/TERM */ static void setup_signals(void) { +#ifndef WIN32 struct sigaction sa; sigemptyset(&nut_upslog_sigmask); @@ -97,6 +106,7 @@ static void setup_signals(void) fatal_with_errno(EXIT_FAILURE, "Can't install SIGQUIT handler"); if (sigaction(SIGTERM, &sa, NULL) < 0) fatal_with_errno(EXIT_FAILURE, "Can't install SIGTERM handler"); +#endif } static void help(const char *prog) @@ -393,6 +403,9 @@ int main(int argc, char **argv) case 'l': logfn = optarg; +#ifdef WIN32 + logfn = filter_path(optarg); +#endif break; case 'i': @@ -430,6 +443,9 @@ int main(int argc, char **argv) if (argc >= 3) { monhost = argv[0]; logfn = argv[1]; +#ifdef WIN32 + logfn = filter_path(argv[1]); +#endif interval = atoi(argv[2]); } diff --git a/clients/upsmon.c b/clients/upsmon.c index 12823fa1ee..5a46405a10 100644 --- a/clients/upsmon.c +++ b/clients/upsmon.c @@ -22,8 +22,12 @@ #include "common.h" #include +#ifndef WIN32 #include #include +#else +#include +#endif #include "upsclient.h" #include "upsmon.h" @@ -73,15 +77,24 @@ static char *certpasswd = NULL; static int certverify = 0; /* don't verify by default */ static int forcessl = 0; /* don't require ssl by default */ -static int userfsd = 0, use_pipe = 1, pipefd[2]; +static int userfsd = 0, pipefd[2]; +#ifndef WIN32 +static int use_pipe = 1; +#else + /* Do not fork in WIN32 */ +static int use_pipe = 0; +static HANDLE mutex = INVALID_HANDLE_VALUE; +#endif static utype_t *firstups = NULL; static int opt_af = AF_UNSPEC; +#ifndef WIN32 /* signal handling things */ static struct sigaction sa; static sigset_t nut_upsmon_sigmask; +#endif static void setflag(int *val, int flag) { @@ -100,6 +113,7 @@ static int flag_isset(int num, int flag) static void wall(const char *text) { +#ifndef WIN32 FILE *wf; wf = popen("wall", "w"); @@ -111,13 +125,82 @@ static void wall(const char *text) fprintf(wf, "%s\n", text); pclose(wf); +#else + #define MESSAGE_CMD "message.exe" + char * command; + + /* first +1 is for the space between message and text + second +1 is for trailing 0 + +2 is for "" */ + command = malloc (strlen(MESSAGE_CMD) + 1 + 2 + strlen(text) + 1); + if( command == NULL ) { + upslog_with_errno(LOG_NOTICE, "Not enough memory for wall"); + return; + } + + sprintf(command,"%s \"%s\"",MESSAGE_CMD,text); + if ( system(command) != 0 ) { + upslog_with_errno(LOG_NOTICE, "Can't invoke wall"); + } + free(command); +#endif } +#ifdef WIN32 +typedef struct async_notify_s { + char *notice; + int flags; + char *ntype; + char *upsname; + char *date; } async_notify_t; + +static unsigned __stdcall async_notify(LPVOID param) +{ + char exec[LARGEBUF]; + char notice[LARGEBUF]; + + /* the following code is a copy of the content of the NOT WIN32 part of + "notify" function below */ + + async_notify_t *data = (async_notify_t *)param; + + if (flag_isset(data->flags, NOTIFY_WALL)) { + snprintf(notice,LARGEBUF,"%s: %s", data->date, data->notice); + wall(notice); + } + + if (flag_isset(data->flags, NOTIFY_EXEC)) { + if (notifycmd != NULL) { + snprintf(exec, sizeof(exec), "%s \"%s\"", notifycmd, data->notice); + + if (data->upsname) + setenv("UPSNAME", data->upsname, 1); + else + setenv("UPSNAME", "", 1); + + setenv("NOTIFYTYPE", data->ntype, 1); + if (system(exec) == -1) { + upslog_with_errno(LOG_ERR, "%s", __func__); + } + } + } + + free(data->notice); + free(data->ntype); + free(data->upsname); + free(data->date); + free(data); + return 1; +} +#endif + static void notify(const char *notice, int flags, const char *ntype, const char *upsname) { +#ifndef WIN32 char exec[LARGEBUF]; int ret; +#endif if (flag_isset(flags, NOTIFY_IGNORE)) return; @@ -125,6 +208,7 @@ static void notify(const char *notice, int flags, const char *ntype, if (flag_isset(flags, NOTIFY_SYSLOG)) upslogx(LOG_NOTICE, "%s", notice); +#ifndef WIN32 /* fork here so upsmon doesn't get wedged if the notifier is slow */ ret = fork(); @@ -158,6 +242,27 @@ static void notify(const char *notice, int flags, const char *ntype, } exit(EXIT_SUCCESS); +#else + async_notify_t * data; + time_t t; + + data = malloc(sizeof(async_notify_t)); + data->notice = strdup(notice); + data->flags = flags; + data->ntype = strdup(ntype); + data->upsname = strdup(upsname); + t = time(NULL); + data->date = strdup(ctime(&t)); + + _beginthreadex( + NULL, /* security FIXME */ + 0, /* stack size */ + async_notify, + (void *)data, + 0, /* Creation flags */ + NULL /* thread id */ + ); +#endif } static void do_notify(const utype_t *ups, int ntype) @@ -437,11 +542,39 @@ static void doshutdown(void) } else { /* one process model = we do all the work here */ +#ifndef WIN32 if (geteuid() != 0) upslogx(LOG_WARNING, "Not root, shutdown may fail"); +#endif set_pdflag(); +#ifdef WIN32 + SC_HANDLE SCManager; + SC_HANDLE Service; + SERVICE_STATUS Status; + + SCManager = OpenSCManager( + NULL, /* local computer */ + NULL, /* ServiceActive database */ + SC_MANAGER_ALL_ACCESS); /* full access rights */ + + if (NULL == SCManager) { + upslogx(LOG_ERR, "OpenSCManager failed (%d)\n", (int)GetLastError()); + } + else { + Service = OpenService(SCManager,SVCNAME,SERVICE_STOP); + if( Service == NULL ) { + upslogx(LOG_ERR,"OpenService failed (%d)\n", (int)GetLastError()); + } + else { + ControlService(Service,SERVICE_CONTROL_STOP,&Status); + /* Give time to the service to stop */ + Sleep(2000); + } + } +#endif + ret = system(shutdowncmd); if (ret != 0) @@ -494,13 +627,17 @@ static void setfsd(utype_t *ups) static void set_alarm(void) { +#ifndef WIN32 alarm(NET_TIMEOUT); +#endif } static void clear_alarm(void) { +#ifndef WIN32 signal(SIGALRM, SIG_IGN); alarm(0); +#endif } static int get_var(utype_t *ups, const char *var, char *buf, size_t bufsize) @@ -1074,7 +1211,11 @@ static int parse_conf_arg(int numargs, char **arg) checkmode(arg[0], powerdownflag, arg[1], reload_flag); free(powerdownflag); +#ifndef WIN32 powerdownflag = xstrdup(arg[1]); +#else + powerdownflag = filter_path(arg[1]); +#endif if (!reload_flag) upslogx(LOG_INFO, "Using power down flag file %s", @@ -1270,11 +1411,13 @@ static void loadconfig(void) pconf_finish(&ctx); } +#ifndef WIN32 /* SIGPIPE handler */ static void sigpipe(int sig) { upsdebugx(1, "SIGPIPE: dazed and confused, but continuing..."); } +#endif /* SIGQUIT, SIGTERM handler */ static void set_exit_flag(int sig) @@ -1319,6 +1462,13 @@ static void upsmon_cleanup(void) } upscli_cleanup(); + +#ifdef WIN32 + if(mutex != INVALID_HANDLE_VALUE) { + ReleaseMutex(mutex); + CloseHandle(mutex); + } +#endif } static void user_fsd(int sig) @@ -1332,15 +1482,18 @@ static void set_reload_flag(int sig) reload_flag = 1; } +#ifndef WIN32 /* handler for alarm when getupsvarfd times out */ static void read_timeout(int sig) { /* don't do anything here, just return */ } +#endif /* install handlers for a few signals */ static void setup_signals(void) { +#ifndef WIN32 sigemptyset(&nut_upsmon_sigmask); sa.sa_mask = nut_upsmon_sigmask; sa.sa_flags = 0; @@ -1365,6 +1518,9 @@ static void setup_signals(void) sa.sa_handler = set_reload_flag; sigaction(SIGCMD_RELOAD, &sa, NULL); +#else + pipe_create(UPSMON_PIPE_NAME); +#endif } /* remember the last time the ups was not critical (OB + LB) */ @@ -1655,15 +1811,18 @@ static void help(const char *progname) exit(EXIT_SUCCESS); } +#ifndef WIN32 static void runparent(int fd) { int ret; char ch; +#ifndef WIN32 /* handling signals is the child's job */ signal(SIGHUP, SIG_IGN); signal(SIGUSR1, SIG_IGN); signal(SIGUSR2, SIG_IGN); +#endif ret = read(fd, &ch, 1); @@ -1689,10 +1848,12 @@ static void runparent(int fd) close(fd); exit(EXIT_SUCCESS); } +#endif /* fire up the split parent/child scheme */ static void start_pipe(void) { +#ifndef WIN32 int ret; ret = pipe(pipefd); @@ -1714,6 +1875,7 @@ static void start_pipe(void) } close(pipefd[0]); +#endif } static void delete_ups(utype_t *target) @@ -1859,7 +2021,35 @@ static void check_parent(void) int main(int argc, char *argv[]) { const char *prog = xbasename(argv[0]); - int i, cmd = 0, checking_flag = 0; + +#ifdef WIN32 + HANDLE handles[MAXIMUM_WAIT_OBJECTS]; + int maxhandle = 0; + pipe_conn_t *conn; + + /* remove trailing .exe */ + char * drv_name; + drv_name = (char *)xbasename(argv[0]); + char * name = strrchr(drv_name,'.'); + if( name != NULL ) { + if(strcasecmp(name, ".exe") == 0 ) { + prog = strdup(drv_name); + char * t = strrchr(prog,'.'); + *t = 0; + } + } + else { + prog = drv_name; + } +#endif + + int i, checking_flag = 0; +#ifndef WIN32 + int cmd = 0; +#else + const char * cmd = NULL; + DWORD ret; +#endif printf("Network UPS Tools %s %s\n", prog, UPS_VERSION); @@ -1920,14 +2110,29 @@ int main(int argc, char *argv[]) } if (cmd) { +#ifndef WIN32 sendsignal(prog, cmd); +#else + sendsignal(UPSMON_PIPE_NAME, cmd); +#endif exit(EXIT_SUCCESS); } /* otherwise, we are being asked to start. * so check if a previous instance is running by sending signal '0' * (Ie 'kill 0') */ +#ifndef WIN32 if (sendsignal(prog, 0) == 0) { +#else + mutex = CreateMutex(NULL,TRUE,UPSMON_PIPE_NAME); + if(mutex == NULL ) { + if( GetLastError() != ERROR_ACCESS_DENIED ) { + fatalx(EXIT_FAILURE, "Can not create mutex %s : %d.\n",UPSMON_PIPE_NAME,(int)GetLastError()); + } + } + + if (GetLastError() == ERROR_ALREADY_EXISTS || GetLastError() == ERROR_ACCESS_DENIED) { +#endif printf("Fatal error: A previous upsmon instance is already running!\n"); printf("Either stop the previous instance first, or use the 'reload' command.\n"); exit(EXIT_FAILURE); @@ -1980,7 +2185,9 @@ int main(int argc, char *argv[]) become_user(new_uid); } else { +#ifndef WIN32 upslogx(LOG_INFO, "Warning: running as one big root process by request (upsmon -p)"); +#endif writepid(prog); } @@ -2015,10 +2222,70 @@ int main(int argc, char *argv[]) if (use_pipe) check_parent(); +#ifndef WIN32 /* reap children that have exited */ waitpid(-1, NULL, WNOHANG); sleep(sleepval); +#else + maxhandle = 0; + memset(&handles,0,sizeof(handles)); + + /* Wait on the read IO of each connections */ + for (conn = pipe_connhead; conn; conn = conn->next) { + handles[maxhandle] = conn->overlapped.hEvent; + maxhandle++; + } + /* Add the new pipe connected event */ + handles[maxhandle] = pipe_connection_overlapped.hEvent; + maxhandle++; + + ret = WaitForMultipleObjects(maxhandle,handles,FALSE,sleepval*1000); + + if (ret == WAIT_FAILED) { + upslogx(LOG_ERR, "Wait failed"); + exit(EXIT_FAILURE); + } + + if (ret == WAIT_TIMEOUT) { + continue; + } + + /* Retrieve the signaled connection */ + for(conn = pipe_connhead; conn != NULL; conn = conn->next) { + if( conn->overlapped.hEvent == handles[ret-WAIT_OBJECT_0]) { + break; + } + } + /* a new pipe connection has been signaled */ + if (handles[ret] == pipe_connection_overlapped.hEvent) { + pipe_connect(); + } + /* one of the read event handle has been signaled */ + else { + if( conn != NULL) { + if ( pipe_ready(conn) ) { + if (!strncmp(conn->buf, SIGCMD_FSD, sizeof(SIGCMD_FSD))) { + user_fsd(1); + } + + else if (!strncmp(conn->buf, SIGCMD_RELOAD, sizeof(SIGCMD_RELOAD))) { + set_reload_flag(1); + } + + else if (!strncmp(conn->buf, SIGCMD_STOP, sizeof(SIGCMD_STOP))) { + set_exit_flag(1); + } + + else { + upslogx(LOG_ERR,"Unknown signal"); + } + + pipe_disconnect(conn); + } + } + } +#endif } upslogx(LOG_INFO, "Signal %d: exiting", exit_flag); diff --git a/clients/upsmon.h b/clients/upsmon.h index 2504370cf1..5cb70b8401 100644 --- a/clients/upsmon.h +++ b/clients/upsmon.h @@ -17,6 +17,11 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#ifdef WIN32 +/* ST_CONNECTED is defined for WIN32 DDE (Dynamic Data Exchange) in ddeml.h */ +#undef ST_CONNECTED +#endif + /* flags for ups->status */ #define ST_ONLINE (1 << 0) /* UPS is on line (OL) */ @@ -83,8 +88,15 @@ typedef struct { #define NOTIFY_WALL (1 << 2) /* send the msg to all users */ #define NOTIFY_EXEC (1 << 3) /* send the msg to NOTIFYCMD script */ -/* flags are set to NOTIFY_SYSLOG | NOTIFY_WALL at program init */ -/* the user can override with NOTIFYFLAGS in the upsmon.conf */ +/* flags are set to NOTIFY_SYSLOG | NOTIFY_WALL at program init */ +/* except under Windows where they are set to NOTIFY_SYSLOG only */ +/* the user can override with NOTIFYFLAGS in the upsmon.conf */ + +#ifdef WIN32 +#define NOTIFY_DEFAULT NOTIFY_SYSLOG +#else +#define NOTIFY_DEFAULT (NOTIFY_SYSLOG | NOTIFY_WALL) +#endif struct { int type; @@ -94,24 +106,30 @@ struct { int flags; } notifylist[] = { - { NOTIFY_ONLINE, "ONLINE", NULL, "UPS %s on line power", NOTIFY_SYSLOG | NOTIFY_WALL }, - { NOTIFY_ONBATT, "ONBATT", NULL, "UPS %s on battery", NOTIFY_SYSLOG | NOTIFY_WALL }, - { NOTIFY_LOWBATT, "LOWBATT", NULL, "UPS %s battery is low", NOTIFY_SYSLOG | NOTIFY_WALL }, - { NOTIFY_FSD, "FSD", NULL, "UPS %s: forced shutdown in progress", NOTIFY_SYSLOG | NOTIFY_WALL }, - { NOTIFY_COMMOK, "COMMOK", NULL, "Communications with UPS %s established", NOTIFY_SYSLOG | NOTIFY_WALL }, - { NOTIFY_COMMBAD, "COMMBAD", NULL, "Communications with UPS %s lost", NOTIFY_SYSLOG | NOTIFY_WALL }, - { NOTIFY_SHUTDOWN, "SHUTDOWN", NULL, "Auto logout and shutdown proceeding", NOTIFY_SYSLOG | NOTIFY_WALL }, - { NOTIFY_REPLBATT, "REPLBATT", NULL, "UPS %s battery needs to be replaced", NOTIFY_SYSLOG | NOTIFY_WALL }, - { NOTIFY_NOCOMM, "NOCOMM", NULL, "UPS %s is unavailable", NOTIFY_SYSLOG | NOTIFY_WALL }, - { NOTIFY_NOPARENT, "NOPARENT", NULL, "upsmon parent process died - shutdown impossible", NOTIFY_SYSLOG | NOTIFY_WALL }, + { NOTIFY_ONLINE, "ONLINE", NULL, "UPS %s on line power", NOTIFY_DEFAULT }, + { NOTIFY_ONBATT, "ONBATT", NULL, "UPS %s on battery", NOTIFY_DEFAULT }, + { NOTIFY_LOWBATT, "LOWBATT", NULL, "UPS %s battery is low", NOTIFY_DEFAULT }, + { NOTIFY_FSD, "FSD", NULL, "UPS %s: forced shutdown in progress", NOTIFY_DEFAULT }, + { NOTIFY_COMMOK, "COMMOK", NULL, "Communications with UPS %s established", NOTIFY_DEFAULT }, + { NOTIFY_COMMBAD, "COMMBAD", NULL, "Communications with UPS %s lost", NOTIFY_DEFAULT }, + { NOTIFY_SHUTDOWN, "SHUTDOWN", NULL, "Auto logout and shutdown proceeding", NOTIFY_DEFAULT }, + { NOTIFY_REPLBATT, "REPLBATT", NULL, "UPS %s battery needs to be replaced", NOTIFY_DEFAULT }, + { NOTIFY_NOCOMM, "NOCOMM", NULL, "UPS %s is unavailable", NOTIFY_DEFAULT }, + { NOTIFY_NOPARENT, "NOPARENT", NULL, "upsmon parent process died - shutdown impossible", NOTIFY_DEFAULT }, { 0, NULL, NULL, NULL, 0 } }; /* values for signals passed between processes */ +#ifndef WIN32 #define SIGCMD_FSD SIGUSR1 #define SIGCMD_STOP SIGTERM #define SIGCMD_RELOAD SIGHUP +#else +#define SIGCMD_FSD COMMAND_FSD +#define SIGCMD_STOP COMMAND_STOP +#define SIGCMD_RELOAD COMMAND_RELOAD +#endif /* various constants */ diff --git a/clients/upsrw.c b/clients/upsrw.c index 8113690414..1feb6ee581 100644 --- a/clients/upsrw.c +++ b/clients/upsrw.c @@ -20,10 +20,14 @@ #include "common.h" #include "nut_platform.h" +#ifndef WIN32 #include #include #include #include +#else +#include "wincompat.h" +#endif #include "upsclient.h" diff --git a/clients/upssched.c b/clients/upssched.c index 97b3ed42db..fda3452bd9 100644 --- a/clients/upssched.c +++ b/clients/upssched.c @@ -42,10 +42,16 @@ #include "common.h" #include +#ifndef WIN32 #include #include #include #include +#else +#include "wincompat.h" +#include +#include +#endif #include "upssched.h" #include "timehead.h" @@ -65,6 +71,11 @@ typedef struct ttype_s { /* ups name and notify type (string) as received from upsmon */ const char *upsname, *notify_type; +#ifdef WIN32 +static OVERLAPPED connect_overlapped; +#define BUF_LEN 512 +#endif + #define PARENT_STARTED -2 #define PARENT_UNNECESSARY -3 #define MAX_TRIES 30 @@ -83,6 +94,7 @@ static void exec_cmd(const char *cmd) snprintf(buf, sizeof(buf), "%s %s", cmdscript, cmd); err = system(buf); +#ifndef WIN32 if (WIFEXITED(err)) { if (WEXITSTATUS(err)) { upslogx(LOG_INFO, "exec_cmd(%s) returned %d", buf, WEXITSTATUS(err)); @@ -94,6 +106,14 @@ static void exec_cmd(const char *cmd) upslogx(LOG_ERR, "Execute command failure: %s", buf); } } +#else + if(err != -1) { + upslogx(LOG_INFO, "Execute command \"%s\" OK", buf); + } + else { + upslogx(LOG_ERR, "Execute command failure : %s", buf); + } +#endif return; } @@ -237,6 +257,7 @@ static void cancel_timer(const char *name, const char *cname) } } +#ifndef WIN32 static void us_serialize(int op) { static int pipefd[2]; @@ -264,7 +285,9 @@ static void us_serialize(int op) break; } } +#endif +#ifndef WIN32 static int open_sock(void) { int ret, fd; @@ -299,6 +322,44 @@ static int open_sock(void) return fd; } +#else +static HANDLE open_sock(void) +{ + HANDLE fd; + + fd = CreateNamedPipe( + pipefn, /* pipe name */ + PIPE_ACCESS_DUPLEX | /* read/write access */ + FILE_FLAG_OVERLAPPED, /* async IO */ + PIPE_TYPE_BYTE | + PIPE_READMODE_BYTE | + PIPE_WAIT, + PIPE_UNLIMITED_INSTANCES, /* max. instances */ + BUF_LEN, /* output buffer size */ + BUF_LEN, /* input buffer size */ + 0, /* client time-out */ + NULL); /* FIXME: default security attributes */ + + if (fd == INVALID_HANDLE_VALUE) { + fatal_with_errno(EXIT_FAILURE, "Can't create a state socket (windows named pipe)"); + } + + /* Prepare an async wait on a connection on the pipe */ + memset(&connect_overlapped,0,sizeof(connect_overlapped)); + connect_overlapped.hEvent = CreateEvent(NULL, /*Security*/ + FALSE, /* auto-reset*/ + FALSE, /* inital state = non signaled*/ + NULL /* no name*/); + if(connect_overlapped.hEvent == NULL ) { + fatal_with_errno(EXIT_FAILURE, "Can't create event"); + } + + /* Wait for a connection */ + ConnectNamedPipe(fd,&connect_overlapped); + + return fd; +} +#endif static void conn_del(conn_t *target) { @@ -337,20 +398,55 @@ static int send_to_one(conn_t *conn, const char *fmt, ...) vsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); +#ifndef WIN32 ret = write(conn->fd, buf, strlen(buf)); if ((ret < 1) || (ret != (int) strlen(buf))) { upsdebugx(2, "write to fd %d failed", conn->fd); - close(conn->fd); conn_del(conn); return 0; /* failed */ } +#else + DWORD bytesWritten = 0; + BOOL result = FALSE; + + result = WriteFile (conn->fd,buf,strlen(buf),&bytesWritten,NULL); + if( result == 0 ) { + upsdebugx(2, "write failed on %d, disconnecting", (int)conn->fd); + /* FIXME not sure this is the right way to close a connection */ + if( conn->read_overlapped.hEvent != INVALID_HANDLE_VALUE) { + CloseHandle(conn->read_overlapped.hEvent); + conn->read_overlapped.hEvent = INVALID_HANDLE_VALUE; + } + DisconnectNamedPipe(conn->fd); + CloseHandle(conn->fd); + conn_del(conn); + return 0; + } + else { + ret = (int)bytesWritten; + } + + if ((ret < 1) || (ret != (int) strlen(buf))) { + upsdebugx(2, "write to fd %p failed", conn->fd); + /* FIXME not sure this is the right way to close a connection */ + if( conn->read_overlapped.hEvent != INVALID_HANDLE_VALUE) { + CloseHandle(conn->read_overlapped.hEvent); + conn->read_overlapped.hEvent = INVALID_HANDLE_VALUE; + } + DisconnectNamedPipe(conn->fd); + CloseHandle(conn->fd); + + return 0; /* failed */ + } +#endif return 1; /* OK */ } +#ifndef WIN32 static void conn_add(int sockfd) { int acc, ret; @@ -408,6 +504,87 @@ static void conn_add(int sockfd) pconf_init(&tmp->ctx, NULL); } +#else +static HANDLE conn_add(HANDLE sockfd) +{ + HANDLE acc; + conn_t * conn; + conn_t *tmp, *last; + + /* We have detected a connection on the opened pipe. So we start + by saving its handle and create a new pipe for future connection */ + conn = xcalloc(1, sizeof(*conn)); + conn->fd = sockfd; + + /* sock is the handle of the connection pending pipe */ + acc = CreateNamedPipe( + pipefn, /* pipe name */ + PIPE_ACCESS_DUPLEX | /* read/write access */ + FILE_FLAG_OVERLAPPED, /* async IO */ + PIPE_TYPE_BYTE | + PIPE_READMODE_BYTE | + PIPE_WAIT, + PIPE_UNLIMITED_INSTANCES, /* max. instances */ + BUF_LEN, /* output buffer size */ + BUF_LEN, /* input buffer size */ + 0, /* client time-out */ + NULL); /* FIXME: default security attribute */ + + if (acc == INVALID_HANDLE_VALUE) { + fatal_with_errno(EXIT_FAILURE, "Can't create a state socket (windows named pipe)"); + } + + /* Prepare a new async wait for a connection on the pipe */ + CloseHandle(connect_overlapped.hEvent); + memset(&connect_overlapped,0,sizeof(connect_overlapped)); + connect_overlapped.hEvent = CreateEvent(NULL, /*Security*/ + FALSE, /* auto-reset*/ + FALSE, /* inital state = non signaled*/ + NULL /* no name*/); + if(connect_overlapped.hEvent == NULL ) { + fatal_with_errno(EXIT_FAILURE, "Can't create event"); + } + + /* Wait for a connection */ + ConnectNamedPipe(acc,&connect_overlapped); + + /* A new pipe waiting for new client connection has been created. + We could manage the current connection now */ + /* Start a read operation on the newly connected pipe so we could wait + on the event associated to this IO */ + memset(&conn->read_overlapped,0,sizeof(conn->read_overlapped)); + memset(conn->buf,0,sizeof(conn->buf)); + conn->read_overlapped.hEvent = CreateEvent(NULL, /*Security*/ + FALSE, /* auto-reset*/ + FALSE, /* inital state = non signaled*/ + NULL /* no name*/); + if(conn->read_overlapped.hEvent == NULL ) { + fatal_with_errno(EXIT_FAILURE, "Can't create event"); + } + + ReadFile (conn->fd,conn->buf,1,NULL,&(conn->read_overlapped)); + + conn->next = NULL; + + tmp = last = connhead; + + while (tmp) { + last = tmp; + tmp = tmp->next; + } + + if (last) + last->next = conn; + else + connhead = conn; + + upsdebugx(3, "new connection on fd %p", acc); + + pconf_init(&conn->ctx, NULL); + + return acc; +} +#endif static int sock_arg(conn_t *conn) { @@ -457,6 +634,7 @@ static int sock_read(conn_t *conn) for (i = 0; i < US_MAX_READ; i++) { +#ifndef WIN32 ret = read(conn->fd, &ch, 1); if (ret < 1) { @@ -468,7 +646,22 @@ static int sock_read(conn_t *conn) /* some other problem */ return -1; /* error */ } +#else + DWORD bytesRead; + GetOverlappedResult(conn->fd, &conn->read_overlapped, &bytesRead,FALSE); + if( bytesRead < 1 ) { + /* Restart async read */ + memset(conn->buf,0,sizeof(conn->buf)); + ReadFile(conn->fd,conn->buf,1,NULL,&(conn->read_overlapped)); + return 0; + } + ch = conn->buf[0]; + + /* Restart async read */ + memset(conn->buf,0,sizeof(conn->buf)); + ReadFile(conn->fd,conn->buf,1,NULL,&(conn->read_overlapped)); +#endif ret = pconf_char(&conn->ctx, ch); if (ret == 0) /* nothing to parse yet */ @@ -493,6 +686,7 @@ static int sock_read(conn_t *conn) return 0; /* fell out without parsing anything */ } +#ifndef WIN32 static void start_daemon(int lockfd) { int maxfd, pid, pipefd, ret; @@ -586,9 +780,99 @@ static void start_daemon(int lockfd) checktimers(); } } +#else +static void start_daemon(HANDLE lockfd) +{ + int maxfd; + HANDLE pipefd; + DWORD timeout_ms; + HANDLE rfds[32]; + struct timeval tv; + conn_t *tmp; + + char module[MAX_PATH]; + STARTUPINFO sinfo; + PROCESS_INFORMATION pinfo; + if( !GetModuleFileName(NULL,module,MAX_PATH) ) { + fatal_with_errno(EXIT_FAILURE, "Can't retrieve module name"); + } + memset(&sinfo,0,sizeof(sinfo)); + if(!CreateProcess(module, NULL, NULL,NULL,FALSE,0,NULL,NULL,&sinfo,&pinfo)) { + fatal_with_errno(EXIT_FAILURE, "Can't create child process"); + } + pipefd = open_sock(); + + if (verbose) + upslogx(LOG_INFO, "Timer daemon started"); + + /* drop the lock now that the background is running */ + CloseHandle(lockfd); + DeleteFile(lockfn); + + /* now watch for activity */ + + for (;;) { + /* wait at most 1s so we can check our timers regularly */ + tv.tv_sec = 1; + tv.tv_usec = 0; + + timeout_ms = (tv.tv_sec * 1000) + (tv.tv_usec / 1000); + + maxfd = 0; + + /* Wait on the read IO of each connections */ + for (tmp = connhead; tmp != NULL; tmp = tmp->next) { + rfds[maxfd] = tmp->read_overlapped.hEvent; + maxfd++; + } + /* Add the connect event */ + rfds[maxfd] = connect_overlapped.hEvent; + maxfd++; + DWORD ret_val; + ret_val = WaitForMultipleObjects( + maxfd, /* number of objects in array */ + rfds, /* array of objects */ + FALSE, /* wait for any object */ + timeout_ms); /* timeout in millisecond */ + + if (ret_val == WAIT_FAILED) { + upslog_with_errno(LOG_ERR, "waitfor failed"); + return; + } + + /* timer has not expired */ + if (ret_val != WAIT_TIMEOUT) { + /* Retrieve the signaled connection */ + for(tmp = connhead; tmp != NULL; tmp = tmp->next) { + if( tmp->read_overlapped.hEvent == rfds[ret_val-WAIT_OBJECT_0]) { + break; + } + } + + /* the connection event handle has been signaled */ + if (rfds[ret_val] == connect_overlapped.hEvent) { + pipefd = conn_add(pipefd); + } + /* one of the read event handle has been signaled */ + else { + if( tmp != NULL) { + if (sock_read(tmp) < 0) { + CloseHandle(tmp->fd); + conn_del(tmp); + } + } + } + + } + + checktimers(); + } +} +#endif /* --- 'client' functions --- */ +#ifndef WIN32 static int try_connect(void) { int pipefd, ret; @@ -610,14 +894,51 @@ static int try_connect(void) return -1; } +#else +static HANDLE try_connect(void) +{ + HANDLE fd; + BOOL result = FALSE; + + result = WaitNamedPipe(pipefn,NMPWAIT_USE_DEFAULT_WAIT); + + if( result == FALSE ) { + return INVALID_HANDLE_VALUE; + } + + fd = CreateFile( + pipefn, /* pipe name */ + GENERIC_READ | /* read and write access */ + GENERIC_WRITE, + 0, /* no sharing */ + NULL, /* default security attributes FIXME */ + OPEN_EXISTING, /* opens existing pipe */ + FILE_FLAG_OVERLAPPED, /* enable async IO */ + NULL); /* no template file */ + + if (fd == INVALID_HANDLE_VALUE) { + return INVALID_HANDLE_VALUE; + } + + return fd; +} +#endif +#ifndef WIN32 static int get_lock(const char *fn) { return open(fn, O_RDONLY | O_CREAT | O_EXCL, 0); } +#else +static HANDLE get_lock(const char *fn) +{ + return CreateFile(fn,GENERIC_ALL,0,NULL,CREATE_NEW,FILE_ATTRIBUTE_NORMAL,NULL); +} +#endif /* try to connect to bg process, and start one if necessary */ +#ifndef WIN32 static int check_parent(const char *cmd, const char *arg2) { int pipefd, lockfd, tries = 0; @@ -658,7 +979,52 @@ static int check_parent(const char *cmd, const char *arg2) upslog_with_errno(LOG_ERR, "Failed to connect to parent and failed to create parent"); exit(EXIT_FAILURE); } +#else +static HANDLE check_parent(const char *cmd, const char *arg2) +{ + int tries = 0; + HANDLE pipefd; + HANDLE lockfd; + + for (tries = 0; tries < MAX_TRIES; tries++) { + + pipefd = try_connect(); + + if (pipefd != INVALID_HANDLE_VALUE) + return pipefd; + + /* timer daemon isn't running */ + + /* it's not running, so there's nothing to cancel */ + if (!strcmp(cmd, "CANCEL") && (arg2 == NULL)) + return (HANDLE)PARENT_UNNECESSARY; + + /* arg2 non-NULL means there is a cancel action available */ + + /* we need to start the daemon, so try to get the lock */ + + lockfd = get_lock(lockfn); + + if (lockfd != INVALID_HANDLE_VALUE) { + start_daemon(lockfd); + return (HANDLE)PARENT_STARTED; /* started successfully */ + } + + /* we didn't get the lock - must be two upsscheds running */ + + /* blow this away in case we crashed before */ + DeleteFile(lockfn); + + /* give the other one a chance to start it, then try again */ + usleep(250000); + } + + upslog_with_errno(LOG_ERR, "Failed to connect to parent and failed to create parent"); + exit(EXIT_FAILURE); +} +#endif +#ifndef WIN32 static void read_timeout(int sig) { /* ignore this */ @@ -676,11 +1042,18 @@ static void setup_sigalrm(void) sa.sa_handler = read_timeout; sigaction(SIGALRM, &sa, NULL); } +#endif static void sendcmd(const char *cmd, const char *arg1, const char *arg2) { - int i, pipefd, ret; + int i, ret; char buf[SMALLBUF], enc[SMALLBUF]; +#ifndef WIN32 + int pipefd; +#else + DWORD bytesWritten = 0; + HANDLE pipefd; +#endif /* insanity */ if (!arg1) @@ -696,6 +1069,7 @@ static void sendcmd(const char *cmd, const char *arg1, const char *arg2) snprintf(enc, sizeof(enc), "%s\n", buf); +#ifndef WIN32 /* see if the parent needs to be started (and maybe start it) */ for (i = 0; i < MAX_TRIES; i++) { @@ -703,7 +1077,6 @@ static void sendcmd(const char *cmd, const char *arg1, const char *arg2) pipefd = check_parent(cmd, arg2); if (pipefd == PARENT_STARTED) { - /* loop back and try to connect now */ usleep(250000); continue; @@ -741,6 +1114,54 @@ static void sendcmd(const char *cmd, const char *arg1, const char *arg2) continue; } +#else + /* see if the parent needs to be started (and maybe start it) */ + + for (i = 0; i < MAX_TRIES; i++) { + + pipefd = check_parent(cmd, arg2); + + if (pipefd == (HANDLE)PARENT_STARTED) { + /* loop back and try to connect now */ + usleep(250000); + continue; + } + + /* special case for CANCEL when no parent is running */ + if (pipefd == (HANDLE)PARENT_UNNECESSARY) + return; + + /* we're connected now */ + ret = WriteFile(pipefd,enc,strlen(enc),&bytesWritten,NULL); + if (ret == 0 || bytesWritten != strlen(enc)) { + upslogx(LOG_ERR, "write failed, trying again"); + CloseHandle(pipefd); + continue; + } + + OVERLAPPED read_overlapped; + DWORD ret; + + memset(&read_overlapped,0,sizeof(read_overlapped)); + memset(buf,0,sizeof(buf)); + read_overlapped.hEvent = CreateEvent(NULL, /*Security*/ + FALSE, /* auto-reset*/ + FALSE, /* inital state = non signaled*/ + NULL /* no name*/); + if(read_overlapped.hEvent == NULL ) { + fatal_with_errno(EXIT_FAILURE, "Can't create event"); + } + + ReadFile(pipefd,buf,sizeof(buf)-1,NULL,&(read_overlapped)); + + ret = WaitForSingleObject(read_overlapped.hEvent,2000); + + if (ret == WAIT_TIMEOUT || ret == WAIT_FAILED) { + upslogx(LOG_ERR, "read confirmation failed, trying again"); + CloseHandle(pipefd); + continue; + } +#endif if (!strncmp(buf, "OK", 2)) return; /* success */ @@ -822,13 +1243,21 @@ static int conf_arg(int numargs, char **arg) /* PIPEFN */ if (!strcmp(arg[0], "PIPEFN")) { +#ifndef WIN32 pipefn = xstrdup(arg[1]); +#else + pipefn = xstrdup("\\\\.\\pipe\\upssched"); +#endif return 1; } /* LOCKFN */ if (!strcmp(arg[0], "LOCKFN")) { +#ifndef WIN32 lockfn = xstrdup(arg[1]); +#else + lockfn = filter_path(arg[1]); +#endif return 1; } diff --git a/clients/upssched.h b/clients/upssched.h index db0c25df4f..24aadddfd8 100644 --- a/clients/upssched.h +++ b/clients/upssched.h @@ -14,7 +14,13 @@ extern "C" { /* track client connections */ typedef struct conn_s { +#ifndef WIN32 int fd; +#else + HANDLE fd; + char buf[LARGEBUF]; + OVERLAPPED read_overlapped; +#endif PCONF_CTX_t ctx; struct conn_s *next; } conn_t; diff --git a/common/Makefile.am b/common/Makefile.am index 6dda04acdd..3ebb7502aa 100644 --- a/common/Makefile.am +++ b/common/Makefile.am @@ -1,6 +1,7 @@ # Network UPS Tools: common AM_CFLAGS = -I$(top_srcdir)/include +AM_LDFLAGS = -no-undefined noinst_LTLIBRARIES = libparseconf.la libcommon.la libparseconf_la_SOURCES = parseconf.c @@ -9,7 +10,7 @@ libparseconf_la_SOURCES = parseconf.c # 'dist', and is only required for actual build, in which case # BUILT_SOURCES (in ../include) will ensure nut_version.h will # be built before anything else -libcommon_la_SOURCES = common.c state.c upsconf.c +libcommon_la_SOURCES = common.c state.c upsconf.c wincompat.c ../include/wincompat.h # ensure inclusion of local implementation of missing systems functions # using LTLIBOBJS. Refer to configure.in -> AC_REPLACE_FUNCS -libcommon_la_LIBADD = libparseconf.la @LTLIBOBJS@ +libcommon_la_LIBADD = libparseconf.la @LTLIBOBJS@ @NETLIBS@ diff --git a/common/common.c b/common/common.c index ef680b0cc2..2cd545da4f 100644 --- a/common/common.c +++ b/common/common.c @@ -20,9 +20,13 @@ #include "common.h" #include +#ifndef WIN32 #include #include #include +#else +#include +#endif /* the reason we define UPS_VERSION as a static string, rather than a macro, is to make dependency tracking easier (only common.o depends @@ -61,6 +65,7 @@ void syslogbit_set(void) /* get the syslog ready for us */ void open_syslog(const char *progname) { +#ifndef WIN32 int opt; opt = LOG_PID; @@ -109,11 +114,15 @@ void open_syslog(const char *progname) break; #endif } +#else + EventLogName = progname; +#endif } /* close ttys and become a daemon */ void background(void) { +#ifndef WIN32 int pid; if ((pid = fork()) < 0) @@ -145,12 +154,17 @@ void background(void) setsid(); /* make a new session to dodge signals */ #endif +#else /* WIN32 */ + xbit_set(&upslog_flags, UPSLOG_SYSLOG); + xbit_clear(&upslog_flags, UPSLOG_STDERR); upslogx(LOG_INFO, "Startup successful"); +#endif } /* do this here to keep pwd/grp stuff out of the main files */ struct passwd *get_user_pwent(const char *name) { +#ifndef WIN32 struct passwd *r; errno = 0; if ((r = getpwnam(name))) @@ -163,13 +177,15 @@ struct passwd *get_user_pwent(const char *name) fatalx(EXIT_FAILURE, "user %s not found", name); else fatal_with_errno(EXIT_FAILURE, "getpwnam(%s)", name); - + +#endif return NULL; /* to make the compiler happy */ } /* change to the user defined in the struct */ void become_user(struct passwd *pw) { +#ifndef WIN32 /* if we can't switch users, then don't even try */ if ((geteuid() != 0) && (getuid() != 0)) return; @@ -186,6 +202,7 @@ void become_user(struct passwd *pw) if (setuid(pw->pw_uid) == -1) fatal_with_errno(EXIT_FAILURE, "setuid"); +#endif } /* drop down into a directory and throw away pointers to the old path */ @@ -194,18 +211,45 @@ void chroot_start(const char *path) if (chdir(path)) fatal_with_errno(EXIT_FAILURE, "chdir(%s)", path); +#ifndef WIN32 if (chroot(path)) fatal_with_errno(EXIT_FAILURE, "chroot(%s)", path); +#endif if (chdir("/")) fatal_with_errno(EXIT_FAILURE, "chdir(/)"); upsdebugx(1, "chrooted into %s", path); } +#ifdef WIN32 +/* In WIN32 all non binaries files (namely configuration and PID files) + are retrieved relative to the path of the binary itself. + So this function fill "dest" with the full path to "relative_path" + depending on the .exe path */ +char * getfullpath(char * relative_path) +{ + char buf[SMALLBUF]; + if ( GetModuleFileName(NULL,buf,SMALLBUF) == 0 ) { + return NULL; + } + + /* remove trailing executable name and its preceeding slash*/ + char * last_slash = strrchr(buf,'\\'); + *last_slash = 0; + + if( relative_path ) { + strncat(buf,relative_path,SMALLBUF); + } + + return(xstrdup(buf)); +} +#endif + /* drop off a pidfile for this process */ void writepid(const char *name) { +#ifndef WIN32 char fn[SMALLBUF]; FILE *pidf; int mask; @@ -213,8 +257,9 @@ void writepid(const char *name) /* use full path if present, else build filename in PIDPATH */ if (*name == '/') snprintf(fn, sizeof(fn), "%s", name); - else + else { snprintf(fn, sizeof(fn), "%s/%s.pid", PIDPATH, name); + } mask = umask(022); pidf = fopen(fn, "w"); @@ -227,9 +272,11 @@ void writepid(const char *name) } umask(mask); +#endif } /* open pidfn, get the pid, then send it sig */ +#ifndef WIN32 int sendsignalfn(const char *pidfn, int sig) { char buf[SMALLBUF]; @@ -277,6 +324,20 @@ int sendsignalfn(const char *pidfn, int sig) fclose(pidf); return 0; } +#else +int sendsignalfn(const char *pidfn, const char * sig) +{ + BOOL ret; + + ret = send_to_named_pipe(pidfn,sig); + + if (ret != 0) { + return -1; + } + + return 0; +} +#endif int snprintfcat(char *dst, size_t size, const char *fmt, ...) { @@ -296,6 +357,7 @@ int snprintfcat(char *dst, size_t size, const char *fmt, ...) } /* lazy way to send a signal if the program uses the PIDPATH */ +#ifndef WIN32 int sendsignal(const char *progname, int sig) { char fn[SMALLBUF]; @@ -304,10 +366,25 @@ int sendsignal(const char *progname, int sig) return sendsignalfn(fn, sig); } +#else +int sendsignal(const char *progname, const char * sig) +{ + return sendsignalfn(progname, sig); +} +#endif const char *xbasename(const char *file) { +#ifndef WIN32 const char *p = strrchr(file, '/'); +#else + const char *p = strrchr(file, '\\'); + const char *r = strrchr(file, '/'); + /* if not found, try '/' */ + if( r > p ) { + p = r; + } +#endif if (p == NULL) return file; @@ -325,8 +402,26 @@ static void vupslog(int priority, const char *fmt, va_list va, int use_strerror) syslog(LOG_WARNING, "vupslog: vsnprintf needed more than %d bytes", LARGEBUF); - if (use_strerror) + if (use_strerror) { snprintfcat(buf, sizeof(buf), ": %s", strerror(errno)); +#ifdef WIN32 + LPVOID WinBuf; + DWORD WinErr = GetLastError(); + FormatMessage( + FORMAT_MESSAGE_MAX_WIDTH_MASK | + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + WinErr, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR) &WinBuf, + 0, NULL ); + + snprintfcat(buf, sizeof(buf), " [%s]", (char *)WinBuf); + LocalFree(WinBuf); +#endif + } if (nut_debug_level > 0) { static struct timeval start = { 0 }; @@ -352,26 +447,33 @@ static void vupslog(int priority, const char *fmt, va_list va, int use_strerror) syslog(priority, "%s", buf); } + /* Return the default path for the directory containing configuration files */ -const char * confpath(void) +const char * confpath(void) { - const char * path; - - if ((path = getenv("NUT_CONFPATH")) == NULL) - path = CONFPATH; - - return path; +#ifndef WIN32 + const char *path = getenv("NUT_CONFPATH"); +#else + static const char *path = NULL; + if (path == NULL) { + path = getfullpath(PATH_ETC); + } +#endif + return (path != NULL) ? path : CONFPATH; } /* Return the default path for the directory containing state files */ -const char * dflt_statepath(void) +const char * dflt_statepath(void) { - const char * path; - - if ((path = getenv("NUT_STATEPATH")) == NULL) - path = STATEPATH; - - return path; +#ifndef WIN32 + const char *path = getenv("NUT_STATEPATH"); +#else + static const char *path = NULL; + if (path == NULL) { + path = getfullpath(PATH_VAR_RUN); + } +#endif + return (path != NULL) ? path : STATEPATH; } /* Return the alternate path for pid files */ @@ -492,6 +594,7 @@ void *xmalloc(size_t size) if (p == NULL) fatal_with_errno(EXIT_FAILURE, "%s", oom_msg); + memset(p,0,size); return p; } @@ -501,6 +604,7 @@ void *xcalloc(size_t number, size_t size) if (p == NULL) fatal_with_errno(EXIT_FAILURE, "%s", oom_msg); + memset(p,0,size*number); return p; } @@ -555,6 +659,7 @@ char* ltrim(char *in, const char sep) /* Read up to buflen bytes from fd and return the number of bytes read. If no data is available within d_sec + d_usec, return 0. On error, a value < 0 is returned (errno indicates error). */ +#ifndef WIN32 int select_read(const int fd, void *buf, const size_t buflen, const long d_sec, const long d_usec) { int ret; @@ -575,12 +680,34 @@ int select_read(const int fd, void *buf, const size_t buflen, const long d_sec, return read(fd, buf, buflen); } +#else +int select_read(serial_handler_t * fd, void *buf, const size_t buflen, const long d_sec, const long d_usec) +{ + /* This function is only called by serial drivers right now */ + int res; + DWORD timeout; + COMMTIMEOUTS TOut; + + timeout = (d_sec*1000) + ((d_usec+999)/1000); + + GetCommTimeouts(fd->handle,&TOut); + TOut.ReadIntervalTimeout = MAXDWORD; + TOut.ReadTotalTimeoutMultiplier = 0; + TOut.ReadTotalTimeoutConstant = timeout; + SetCommTimeouts(fd->handle,&TOut); + + res = w32_serial_read(fd,buf,buflen,timeout); + + return res; +} +#endif /* Write up to buflen bytes to fd and return the number of bytes written. If no data is available within d_sec + d_usec, return 0. On error, a value < 0 is returned (errno indicates error). */ int select_write(const int fd, const void *buf, const size_t buflen, const long d_sec, const long d_usec) { +#ifndef WIN32 int ret; fd_set fds; struct timeval tv; @@ -598,4 +725,7 @@ int select_write(const int fd, const void *buf, const size_t buflen, const long } return write(fd, buf, buflen); +#else + return 0; +#endif } diff --git a/common/snprintf.c b/common/snprintf.c index a1802696b1..6d6f2affbd 100644 --- a/common/snprintf.c +++ b/common/snprintf.c @@ -275,7 +275,7 @@ static void dopr (char *buffer, size_t maxlen, const char *format, va_list args) case 'd': case 'i': if (cflags == DP_C_SHORT) - value = va_arg (args, short int); + value = va_arg (args, int); else if (cflags == DP_C_LONG) value = va_arg (args, long int); else if (cflags == DP_C_LLONG) @@ -287,7 +287,7 @@ static void dopr (char *buffer, size_t maxlen, const char *format, va_list args) case 'o': flags |= DP_F_UNSIGNED; if (cflags == DP_C_SHORT) - value = va_arg (args, unsigned short int); + value = va_arg (args, int); else if (cflags == DP_C_LONG) value = (long)va_arg (args, unsigned long int); else if (cflags == DP_C_LLONG) @@ -299,9 +299,9 @@ static void dopr (char *buffer, size_t maxlen, const char *format, va_list args) case 'u': flags |= DP_F_UNSIGNED; if (cflags == DP_C_SHORT) - value = va_arg (args, unsigned short int); + value = va_arg (args, int); else if (cflags == DP_C_LONG) - value = (long)va_arg (args, unsigned long int); + value = (long)va_arg (args, int); else if (cflags == DP_C_LLONG) value = (LLONG)va_arg (args, unsigned LLONG); else @@ -313,7 +313,7 @@ static void dopr (char *buffer, size_t maxlen, const char *format, va_list args) case 'x': flags |= DP_F_UNSIGNED; if (cflags == DP_C_SHORT) - value = va_arg (args, unsigned short int); + value = va_arg (args, int); else if (cflags == DP_C_LONG) value = (long)va_arg (args, unsigned long int); else if (cflags == DP_C_LLONG) @@ -553,6 +553,7 @@ static LDOUBLE abs_val (LDOUBLE value) return result; } +#ifndef WIN32 static LDOUBLE pow10 (int exp) { LDOUBLE result = 1; @@ -577,6 +578,7 @@ static long round (LDOUBLE value) return intpart; } +#endif static void fmtfp (char *buffer, size_t *currlen, size_t maxlen, LDOUBLE fvalue, int min, int max, int flags) @@ -602,9 +604,6 @@ static void fmtfp (char *buffer, size_t *currlen, size_t maxlen, int fplace = 0; int padlen = 0; /* amount to pad */ int zpadlen = 0; - int caps = 0; - long intpart; - long fracpart; /* * AIX manpage says the default is 0, but Solaris says the default @@ -629,6 +628,10 @@ static void fmtfp (char *buffer, size_t *currlen, size_t maxlen, #endif #ifndef HAVE_FCVT + long fracpart; + long intpart; + int caps = 0; + intpart = (long)ufvalue; /* diff --git a/common/state.c b/common/state.c index cb79e0fcf8..defcd591d1 100644 --- a/common/state.c +++ b/common/state.c @@ -24,8 +24,10 @@ #include #include #include +#ifndef WIN32 #include #include +#endif #include "common.h" #include "state.h" diff --git a/common/wincompat.c b/common/wincompat.c new file mode 100644 index 0000000000..fc3b597d51 --- /dev/null +++ b/common/wincompat.c @@ -0,0 +1,1362 @@ +/* + + Copyright (C) 1999 Russell Kroll + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifdef WIN32 +#include "wincompat.h" + +extern int errno; + +const char * EventLogName = NULL; + +struct passwd wincompat_passwd; +char wincompat_user_name[SMALLBUF]; +char wincompat_password[SMALLBUF]; + +uid_t getuid(void) +{ + DWORD size = sizeof(wincompat_user_name); + if( !GetUserName(wincompat_user_name,&size) ) { + return NULL; + } + + return wincompat_user_name; +} + +struct passwd *getpwuid(uid_t uid) +{ + wincompat_passwd.pw_name = uid; + wincompat_passwd.pw_uid = 0; + return &wincompat_passwd; +} + +char *getpass( const char *prompt) +{ + HANDLE hStdin; + DWORD mode; + + hStdin = GetStdHandle(STD_INPUT_HANDLE); + if(hStdin == INVALID_HANDLE_VALUE) { + return NULL; + } + + printf("%s",prompt); + + GetConsoleMode( hStdin, &mode ); + mode &= ~ENABLE_ECHO_INPUT; + SetConsoleMode( hStdin , mode); + + if (fgets(wincompat_password, sizeof(wincompat_password), stdin) == NULL) { + upsdebug_with_errno(LOG_INFO, "%s", __func__); + return NULL; + } + + /* deal with that pesky newline */ + if (strlen(wincompat_password) > 1) { + wincompat_password[strlen(wincompat_password) - 1] = '\0'; + }; + + hStdin = GetStdHandle(STD_INPUT_HANDLE); + GetConsoleMode( hStdin, &mode ); + mode |= ENABLE_ECHO_INPUT; + SetConsoleMode( hStdin , mode); + + return wincompat_password; +} + +#ifndef HAVE_USLEEP +/* Verbatim from +http://cygwin.com/cgi-bin/cvsweb.cgi/~checkout~/src/winsup/mingw/mingwex/usleep.c?rev=1.2&cvsroot=src */ +int __cdecl usleep(unsigned int useconds) +{ + if(useconds == 0) + return 0; + + if(useconds >= 1000000) + return EINVAL; + + Sleep((useconds + 999) / 1000); + + return 0; +} +#endif /* !HAVE_USLEEP */ + +char * strtok_r(char *str, const char *delim, char **saveptr) +{ + char *token_start, *token_end; + + /* Subsequent call ? */ + token_start = str ? str : *saveptr; + + /* Skip delim characters */ + token_start += strspn(token_start, delim); + if (*token_start == '\0') { + /* No more token */ + *saveptr = ""; + return NULL; + } + + /* Skip NO delim characters */ + token_end = token_start + strcspn(token_start, delim); + + /* Prepare token to be a null terminated string */ + if (*token_end != '\0') + *token_end++ = '\0'; + + *saveptr = token_end; + + return token_start; +} + +int sktconnect(int fh, struct sockaddr * name, int len) +{ + int ret = connect(fh,name,len); + errno = WSAGetLastError(); + return ret; +} +int sktread(int fh, char *buf, int size) +{ + int ret = recv(fh,buf,size,0); + errno = WSAGetLastError(); + return ret; +} +int sktwrite(int fh, char *buf, int size) +{ + int ret = send(fh,buf,size,0); + errno = WSAGetLastError(); + return ret; +} +int sktclose(int fh) +{ + int ret = closesocket((SOCKET)fh); + errno = WSAGetLastError(); + return ret; +} + +const char* inet_ntop(int af, const void* src, char* dst, int cnt){ + struct sockaddr_in srcaddr; + + memset(&srcaddr, 0, sizeof(struct sockaddr_in)); + memcpy(&(srcaddr.sin_addr), src, sizeof(srcaddr.sin_addr)); + + srcaddr.sin_family = af; + if (WSAAddressToString((struct sockaddr*) &srcaddr, sizeof(struct sockaddr_in), 0, dst, (LPDWORD) &cnt) != 0) { + WSAGetLastError(); + return NULL; + } + return dst; +} + +/* "system" call seems to handle path with blank name incorrectly */ +int win_system(const char * command) +{ + BOOL res; + STARTUPINFO si; + PROCESS_INFORMATION pi; + + memset(&si,0,sizeof(si)); + si.cb = sizeof(si); + memset(&pi,0,sizeof(pi)); + + res = CreateProcess(NULL,(char *)command,NULL,NULL,FALSE,0,NULL,NULL,&si,&pi); + + if( res != 0 ) { + return 0; + } + + return -1; +} + +/* the " character is forbiden in Windows files , so we filter this character +in data file paths to be coherent with command line which require " to +distinguish the command from its parameter. This avoid complicated +explanation in the documentation */ +char * filter_path(const char * source) +{ + char * res; + unsigned int i,j; + + if( source == NULL ) { + return NULL; + } + + res = xmalloc(strlen(source)+1); + for(i=0,j=0;i<=strlen(source);i++) { + if(source[i] != '"') { + res[j] = source[i]; + j++; + } + } + + return res; +} + + +/* syslog sends a message through a pipe to the wininit service. Which is + in charge of adding an event in the Windows event logger. + The message is made of 4 bytes containing the priority followed by an array + of chars containing the message to display (no terminal 0 required here) */ +void syslog(int priority, const char *fmt, ...) +{ + char pipe_name[] = "\\\\.\\pipe\\"EVENTLOG_PIPE_NAME; + char buf1[LARGEBUF+sizeof(DWORD)]; + char buf2[LARGEBUF]; + va_list ap; + HANDLE pipe; + DWORD bytesWritten = 0; + + if( EventLogName == NULL ) { + return; + } + + /* Format message */ + va_start(ap,fmt); + vsnprintf(buf1, sizeof(buf1), fmt, ap); + va_end(ap); + + /* Add progname to the formated message */ + snprintf(buf2,sizeof(buf2),"%s - %s",EventLogName,buf1); + + /* Create the frame */ + /* first 4 bytes are priority */ + memcpy(buf1,&priority,sizeof(DWORD)); + /* then comes the message */ + memcpy(buf1+sizeof(DWORD),buf2,sizeof(buf2)); + + pipe = CreateFile( + pipe_name, /* pipe name */ + GENERIC_WRITE, + 0, /* no sharing */ + NULL, /* default security attributes FIXME */ + OPEN_EXISTING, /* opens existing pipe */ + FILE_FLAG_OVERLAPPED, /* enable async IO */ + NULL); /* no template file */ + + + if (pipe == INVALID_HANDLE_VALUE) { + return; + } + + WriteFile (pipe,buf1,strlen(buf2)+sizeof(DWORD),&bytesWritten,NULL); + + /* testing result is useless. If we have an error and try to report it, + this will probably lead to a call to this function and an infinite + loop */ + CloseHandle(pipe); +} + +/* Signal emulation via NamedPipe */ + +static HANDLE pipe_connection_handle; +OVERLAPPED pipe_connection_overlapped; +pipe_conn_t *pipe_connhead = NULL; +static const char *named_pipe_name=NULL; + +void pipe_create(const char * pipe_name) +{ + BOOL ret; + char pipe_full_name[SMALLBUF]; + + /* save pipe name for further use in pipe_connect */ + if( pipe_name == NULL ) { + if( named_pipe_name == NULL ) { + return; + } + } + else { + named_pipe_name = pipe_name; + } + + snprintf(pipe_full_name,sizeof(pipe_full_name),"\\\\.\\pipe\\%s",named_pipe_name); + + if( pipe_connection_overlapped.hEvent != 0 ) { + CloseHandle(pipe_connection_overlapped.hEvent); + } + memset(&pipe_connection_overlapped,0,sizeof(pipe_connection_overlapped)); + pipe_connection_handle = CreateNamedPipe( + pipe_full_name, + PIPE_ACCESS_INBOUND | /* to server only */ + FILE_FLAG_OVERLAPPED, /* async IO */ + PIPE_TYPE_MESSAGE | + PIPE_READMODE_MESSAGE | + PIPE_WAIT, + PIPE_UNLIMITED_INSTANCES, /* max. instances */ + LARGEBUF, /* output buffer size */ + LARGEBUF, /* input buffer size */ + 0, /* client time-out */ + NULL); /* FIXME: default security attribute */ + + if (pipe_connection_handle == INVALID_HANDLE_VALUE) { + upslogx(LOG_ERR,"Error creating named pipe"); + fatal_with_errno(EXIT_FAILURE, "Can't create a state socket (windows named pipe)"); + } + + /* Prepare an async wait on a connection on the pipe */ + pipe_connection_overlapped.hEvent = CreateEvent(NULL, /*Security*/ + FALSE, /* auto-reset*/ + FALSE, /* inital state = non signaled*/ + NULL /* no name*/); + if(pipe_connection_overlapped.hEvent == NULL ) { + upslogx(LOG_ERR,"Error creating event"); + fatal_with_errno(EXIT_FAILURE, "Can't create event"); + } + + /* Wait for a connection */ + ret = ConnectNamedPipe(pipe_connection_handle,&pipe_connection_overlapped); + if(ret == 0 && GetLastError() != ERROR_IO_PENDING ) { + upslogx(LOG_ERR,"ConnectNamedPipe error"); + } +} + +void pipe_connect() +{ + /* We have detected a connection on the opened pipe. So we start by saving its handle and create a new pipe for future connections */ + pipe_conn_t *conn; + + conn = xcalloc(1,sizeof(*conn)); + conn->handle = pipe_connection_handle; + + /* restart a new listening pipe */ + pipe_create(NULL); + + /* A new pipe waiting for new client connection has been created. We could manage the current connection now */ + /* Start a read operation on the newly connected pipe so we could wait on the event associated to this IO */ + memset(&conn->overlapped,0,sizeof(conn->overlapped)); + memset(conn->buf,0,sizeof(conn->buf)); + conn->overlapped.hEvent = CreateEvent(NULL, /*Security*/ + FALSE, /* auto-reset*/ + FALSE, /* inital state = non signaled*/ + NULL /* no name*/); + if(conn->overlapped.hEvent == NULL ) { + upslogx(LOG_ERR,"Can't create event for reading event log"); + return; + } + + ReadFile (conn->handle,conn->buf,sizeof(conn->buf)-1,NULL,&(conn->overlapped)); /* -1 to be sure to have a trailling 0 */ + + if (pipe_connhead) { + conn->next = pipe_connhead; + pipe_connhead->prev = conn; + } + + pipe_connhead = conn; +} + +void pipe_disconnect(pipe_conn_t *conn) +{ + if( conn->overlapped.hEvent != INVALID_HANDLE_VALUE) { + CloseHandle(conn->overlapped.hEvent); + conn->overlapped.hEvent = INVALID_HANDLE_VALUE; + } + if( conn->handle != INVALID_HANDLE_VALUE) { + if ( DisconnectNamedPipe(conn->handle) == 0 ) { + upslogx(LOG_ERR,"DisconnectNamedPipe error : %d",(int)GetLastError()); + } + CloseHandle(conn->handle); + conn->handle = INVALID_HANDLE_VALUE; + } + if (conn->prev) { + conn->prev->next = conn->next; + } else { + pipe_connhead = conn->next; + } + + if (conn->next) { + conn->next->prev = conn->prev; + } else { + /* conntail = conn->prev; */ + } + + free(conn); +} + +int pipe_ready(pipe_conn_t *conn) +{ + DWORD bytesRead; + BOOL res; + + res = GetOverlappedResult(conn->handle, &conn->overlapped, &bytesRead, FALSE); + if( res == 0 ) { + upslogx(LOG_ERR, "Pipe read error"); + pipe_disconnect(conn); + return 0; + } + return 1; +} + +/* return 1 on error, 0 if OK */ +int send_to_named_pipe(const char * pipe_name, const char * data) +{ + HANDLE pipe; + BOOL result = FALSE; + DWORD bytesWritten = 0; + char buf[SMALLBUF]; + + snprintf(buf, sizeof(buf), "\\\\.\\pipe\\%s", pipe_name); + + pipe = CreateFile( + buf, + GENERIC_WRITE, + 0, /* no sharing */ + NULL, /* default security attributes FIXME */ + OPEN_EXISTING, /* opens existing pipe */ + FILE_FLAG_OVERLAPPED, /* enable async IO */ + NULL); /* no template file */ + + + if (pipe == INVALID_HANDLE_VALUE) { + return 1; + } + + result = WriteFile (pipe,data,strlen(data)+1,&bytesWritten,NULL); + + if (result == 0 || bytesWritten != strlen(data)+1 ) { + CloseHandle(pipe); + return 1; + } + + CloseHandle(pipe); + return 0; +} + +int w32_setcomm ( serial_handler_t * h, int * flags ) +{ + int ret = 0; + + if( *flags & TIOCM_DTR ) { + if( !EscapeCommFunction(h->handle,SETDTR) ) { + errno = EIO; + ret = -1; + } + } + else { + if( !EscapeCommFunction(h->handle,CLRDTR) ) { + errno = EIO; + ret = -1; + } + } + + if( *flags & TIOCM_RTS ) { + if( !EscapeCommFunction(h->handle,SETRTS) ) { + errno = EIO; + ret = -1; + } + } + else { + if( !EscapeCommFunction(h->handle,CLRRTS) ) { + errno = EIO; + ret = -1; + } + } + + return ret; +} + +int w32_getcomm ( serial_handler_t * h, int * flags ) +{ + BOOL ret_val; + DWORD f; + + ret_val = GetCommModemStatus(h->handle, &f); + if (ret_val == 0) { + errno = EIO; + return -1; + } + + *flags = f; + + return 0; +} + +/* Serial port wrapper inspired by : +http://serial-programming-in-win32-os.blogspot.com/2008/07/convert-linux-code-to-windows-serial.html */ + +void overlapped_setup (serial_handler_t * sh) +{ + memset (&sh->io_status, 0, sizeof (sh->io_status)); + sh->io_status.hEvent = CreateEvent (NULL, TRUE, FALSE, NULL); + sh->overlapped_armed = 0; +} + +int w32_serial_read (serial_handler_t * sh, void *ptr, size_t ulen, DWORD timeout) +{ + int tot; + DWORD num; + HANDLE w4; + DWORD minchars = sh->vmin_ ?: ulen; + + errno = 0; + + w4 = sh->io_status.hEvent; + + upsdebugx(4,"w32_serial_read : ulen %d, vmin_ %d, vtime_ %d, hEvent %p", ulen, sh->vmin_, sh->vtime_,sh->io_status.hEvent); + if (!sh->overlapped_armed) { + SetCommMask (sh->handle, EV_RXCHAR); + ResetEvent (sh->io_status.hEvent); + } + + for (num = 0, tot = 0; ulen; ulen -= num, ptr = (char *)ptr + num) { + DWORD ev; + COMSTAT st; + DWORD inq = 1; + + num = 0; + + if (!sh->vtime_ && !sh->vmin_) { + inq = ulen; + } + else if (sh->vtime_) { + /* non-interruptible -- have to use kernel timeouts + also note that this is not strictly correct. + if vmin > ulen then things won't work right. + sh->overlapped_armed = -1; + */ + inq = ulen; + } + + if (!ClearCommError (sh->handle, &ev, &st)) { + goto err; + } + else if (ev) { + upsdebugx(4,"w32_serial_read : error detected %x", (int)ev); + } + else if (st.cbInQue) { + inq = st.cbInQue; + } + else if (!sh->overlapped_armed) { + if ((size_t)tot >= minchars) { + break; + } + else if (WaitCommEvent (sh->handle, &ev, &sh->io_status)) { + /* WaitCommEvent succeeded */ + if (!ev) { + continue; + } + } + else if (GetLastError () != ERROR_IO_PENDING) { + goto err; + } + else { + sh->overlapped_armed = 1; + switch (WaitForSingleObject (w4,timeout)) { + case WAIT_OBJECT_0: + if (!GetOverlappedResult (sh->handle, &sh->io_status, &num, FALSE)) { + goto err; + } + upsdebugx(4,"w32_serial_read : characters are available on input buffer"); + break; + case WAIT_TIMEOUT: + if(!tot) { + CancelIo(sh->handle); + sh->overlapped_armed = 0; + ResetEvent (sh->io_status.hEvent); + upsdebugx(4,"w32_serial_read : timeout %d ms ellapsed", (int)timeout); + SetLastError(WAIT_TIMEOUT); + errno = 0; + return 0; + } + default: + goto err; + } + } + } + + sh->overlapped_armed = 0; + ResetEvent (sh->io_status.hEvent); + if (inq > ulen) { + inq = ulen; + } + upsdebugx(4,"w32_serial_read : Reading %d characters", (int)inq); + if (ReadFile (sh->handle, ptr, min (inq, ulen), &num, &sh->io_status)) { + /* Got something */; + } + else if (GetLastError () != ERROR_IO_PENDING) { + goto err; + } + else if (!GetOverlappedResult (sh->handle, &sh->io_status, &num, TRUE)) { + goto err; + } + + tot += num; + upsdebugx(4,"w32_serial_read : total characters read = %d", tot); + if (sh->vtime_ || !sh->vmin_ || !num) { + break; + } + continue; + +err: + PurgeComm (sh->handle, PURGE_RXABORT); + upsdebugx(4,"w32_serial_read : err %d",(int)GetLastError()); + if (GetLastError () == ERROR_OPERATION_ABORTED) { + num = 0; + } + else + { + errno = EIO; + tot = -1; + break; + } + } + + return tot; +} + +/* Cover function to WriteFile to provide Posix interface and semantics + (as much as possible). */ +int w32_serial_write (serial_handler_t * sh, const void *ptr, size_t len) +{ + DWORD bytes_written; + OVERLAPPED write_status; + + errno = 0; + + memset (&write_status, 0, sizeof (write_status)); + write_status.hEvent = CreateEvent (NULL, TRUE, FALSE, NULL); + + for (;;) + { + if (WriteFile (sh->handle, ptr, len, &bytes_written, &write_status)) + break; + + switch (GetLastError ()) + { + case ERROR_OPERATION_ABORTED: + continue; + case ERROR_IO_PENDING: + break; + default: + goto err; + } + + if (!GetOverlappedResult (sh->handle, &write_status, &bytes_written, TRUE)) + goto err; + + break; + } + + CloseHandle(write_status.hEvent); + + return bytes_written; + +err: + CloseHandle(write_status.hEvent); + errno = EIO; + return -1; +} + +serial_handler_t * w32_serial_open (const char *name, int flags) +{ + /* flags are currently ignored, it's here just to have the same + interface as POSIX open */ + COMMTIMEOUTS to; + + errno = 0; + + upslogx(LOG_INFO,"w32_serial_open (%s)",name); + + serial_handler_t * sh; + + sh = xmalloc(sizeof(serial_handler_t)); + memset(sh,0,sizeof(serial_handler_t)); + + sh->handle = CreateFile(name,GENERIC_READ|GENERIC_WRITE,0,0,OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,0); + + if(sh->handle == INVALID_HANDLE_VALUE) { + upslogx(LOG_ERR,"could not open %s",name); + errno = EPERM; + return NULL; + } + + SetCommMask (sh->handle, EV_RXCHAR); + + overlapped_setup (sh); + + memset (&to, 0, sizeof (to)); + SetCommTimeouts (sh->handle, &to); + + /* Reset serial port to known state of 9600-8-1-no flow control + on open for better behavior under Win 95. + */ + DCB state; + GetCommState (sh->handle, &state); + upslogx (LOG_INFO,"setting initial state on %s",name); + state.BaudRate = CBR_9600; + state.ByteSize = 8; + state.StopBits = ONESTOPBIT; + state.Parity = NOPARITY; /* FIXME: correct default? */ + state.fBinary = TRUE; /* binary xfer */ + state.EofChar = 0; /* no end-of-data in binary mode */ + state.fNull = FALSE; /* don't discard nulls in binary mode */ + state.fParity = FALSE; /* ignore parity errors */ + state.fErrorChar = FALSE; + state.fTXContinueOnXoff = TRUE; /* separate TX and RX flow control */ + state.fOutX = FALSE; /* disable transmission flow control */ + state.fInX = FALSE; /* disable reception flow control */ + state.XonChar = 0x11; + state.XoffChar = 0x13; + state.fOutxDsrFlow = FALSE; /* disable DSR flow control */ + state.fRtsControl = RTS_CONTROL_ENABLE; /* ignore lead control except + DTR */ + state.fOutxCtsFlow = FALSE; /* disable output flow control */ + state.fDtrControl = DTR_CONTROL_ENABLE; /* assert DTR */ + state.fDsrSensitivity = FALSE; /* don't assert DSR */ + state.fAbortOnError = TRUE; + + if (!SetCommState (sh->handle, &state)) + upslogx (LOG_ERR,"couldn't set initial state for %s",name); + + SetCommMask (sh->handle, EV_RXCHAR); + + upslogx (LOG_INFO,"%p = w32_serial_open (%s)",sh->handle,name); + return sh; +} + +int w32_serial_close (serial_handler_t * sh) +{ + if( sh->io_status.hEvent != INVALID_HANDLE_VALUE ) { + CloseHandle (sh->io_status.hEvent); + } + if( sh->handle != INVALID_HANDLE_VALUE ) { + CloseHandle (sh->handle); + } + free(sh); + + errno = 0; + + return 0; +} + +/* tcsendbreak: POSIX 7.2.2.1 */ +/* Break for 250-500 milliseconds if duration == 0 */ +/* Otherwise, units for duration are undefined */ +int tcsendbreak (serial_handler_t * sh, int duration) +{ + unsigned int sleeptime = 300000; + + errno = 0; + + if (duration > 0) + sleeptime *= duration; + + if (SetCommBreak (sh->handle) == 0) { + errno = EIO; + return -1; + } + + /* FIXME: need to send zero bits during duration */ + usleep (sleeptime); + + if (ClearCommBreak (sh->handle) == 0) { + errno = EIO; + return -1; + } + + upslogx(LOG_DEBUG,"0 = tcsendbreak (%d)", duration); + + return 0; +} + +/* tcdrain: POSIX 7.2.2.1 */ +int tcdrain (serial_handler_t * sh) +{ + errno = 0; + + if (FlushFileBuffers (sh->handle) == 0) { + errno = EIO; + return -1; + } + + return 0; +} + +/* tcflow: POSIX 7.2.2.1 */ +int tcflow (serial_handler_t * sh, int action) +{ + DWORD win32action = 0; + DCB dcb; + char xchar; + + errno = 0; + + upslogx(LOG_DEBUG,"action %d", action); + + switch (action) + { + case TCOOFF: + win32action = SETXOFF; + break; + case TCOON: + win32action = SETXON; + break; + case TCION: + case TCIOFF: + if (GetCommState (sh->handle, &dcb) == 0) + return -1; + if (action == TCION) + xchar = (dcb.XonChar ? dcb.XonChar : 0x11); + else + xchar = (dcb.XoffChar ? dcb.XoffChar : 0x13); + if (TransmitCommChar (sh->handle, xchar) == 0) + return -1; + return 0; + break; + default: + return -1; + break; + } + + if (EscapeCommFunction (sh->handle, win32action) == 0) { + errno = EIO; + return -1; + } + + return 0; +} + +/* tcflush: POSIX 7.2.2.1 */ +int tcflush (serial_handler_t * sh, int queue) +{ + int max; + + errno = 0; + + if (queue == TCOFLUSH || queue == TCIOFLUSH) + PurgeComm (sh->handle, PURGE_TXABORT | PURGE_TXCLEAR); + + if ((queue == TCIFLUSH) | (queue == TCIOFLUSH)) + /* Input flushing by polling until nothing turns up + (we stop after 1000 chars anyway) */ + for (max = 1000; max > 0; max--) + { + DWORD ev; + COMSTAT st; + if (!PurgeComm (sh->handle, PURGE_RXABORT | PURGE_RXCLEAR)) + break; + Sleep (100); + if (!ClearCommError (sh->handle, &ev, &st) || !st.cbInQue) + break; + } + + return 0; +} + +/* tcsetattr: POSIX 7.2.1.1 */ +int tcsetattr (serial_handler_t * sh, int action, const struct termios *t) +{ + /* Possible actions: +TCSANOW: immediately change attributes. +TCSADRAIN: flush output, then change attributes. +TCSAFLUSH: flush output and discard input, then change attributes. + */ + + BOOL dropDTR = FALSE; + COMMTIMEOUTS to; + DCB ostate, state; + unsigned int ovtime = sh->vtime_, ovmin = sh->vmin_; + + errno = 0; + + upslogx(LOG_DEBUG, "action %d", action); + if ((action == TCSADRAIN) || (action == TCSAFLUSH)) + { + FlushFileBuffers (sh->handle); + upslogx(LOG_DEBUG,"flushed file buffers"); + } + if (action == TCSAFLUSH) + PurgeComm (sh->handle, (PURGE_RXABORT | PURGE_RXCLEAR)); + + /* get default/last comm state */ + if (!GetCommState (sh->handle, &ostate)) { + errno = EIO; + return -1; + } + + state = ostate; + + /* -------------- Set baud rate ------------------ */ + /* FIXME: WIN32 also has 14400, 56000, 128000, and 256000. + Unix also has 230400. */ + + switch (t->c_ospeed) + { + case B0: /* drop DTR */ + dropDTR = TRUE; + state.BaudRate = 0; + break; + case B110: + state.BaudRate = CBR_110; + break; + case B300: + state.BaudRate = CBR_300; + break; + case B600: + state.BaudRate = CBR_600; + break; + case B1200: + state.BaudRate = CBR_1200; + break; + case B2400: + state.BaudRate = CBR_2400; + break; + case B4800: + state.BaudRate = CBR_4800; + break; + case B9600: + state.BaudRate = CBR_9600; + break; + case B19200: + state.BaudRate = CBR_19200; + break; + case B38400: + state.BaudRate = CBR_38400; + break; + case B57600: + state.BaudRate = CBR_57600; + break; + case B115200: + state.BaudRate = CBR_115200; + break; + default: + /* Unsupported baud rate! */ + upslogx(LOG_ERR,"Invalid t->c_ospeed %d", t->c_ospeed); + errno = EINVAL; + return -1; + } + + /* -------------- Set byte size ------------------ */ + + switch (t->c_cflag & CSIZE) + { + case CS5: + state.ByteSize = 5; + break; + case CS6: + state.ByteSize = 6; + break; + case CS7: + state.ByteSize = 7; + break; + case CS8: + state.ByteSize = 8; + break; + default: + /* Unsupported byte size! */ + upslogx(LOG_ERR,"Invalid t->c_cflag byte size %d", + t->c_cflag & CSIZE); + errno = EINVAL; + return -1; + } + + /* -------------- Set stop bits ------------------ */ + + if (t->c_cflag & CSTOPB) + state.StopBits = TWOSTOPBITS; + else + state.StopBits = ONESTOPBIT; + + /* -------------- Set parity ------------------ */ + + if (t->c_cflag & PARENB) + state.Parity = (t->c_cflag & PARODD) ? ODDPARITY : EVENPARITY; + else + state.Parity = NOPARITY; + + state.fBinary = TRUE; /* Binary transfer */ + state.EofChar = 0; /* No end-of-data in binary mode */ + state.fNull = FALSE; /* Don't discard nulls in binary mode */ + + /* -------------- Parity errors ------------------ */ + /* fParity combines the function of INPCK and NOT IGNPAR */ + + if ((t->c_iflag & INPCK) && !(t->c_iflag & IGNPAR)) + state.fParity = TRUE; /* detect parity errors */ + else + state.fParity = FALSE; /* ignore parity errors */ + + /* Only present in Win32, Unix has no equivalent */ + state.fErrorChar = FALSE; + state.ErrorChar = 0; + + /* -------------- Set software flow control ------------------ */ + /* Set fTXContinueOnXoff to FALSE. This prevents the triggering of a + premature XON when the remote device interprets a received character + as XON (same as IXANY on the remote side). Otherwise, a TRUE + value separates the TX and RX functions. */ + + state.fTXContinueOnXoff = TRUE; /* separate TX and RX flow control */ + + /* Transmission flow control */ + if (t->c_iflag & IXON) + state.fOutX = TRUE; /* enable */ + else + state.fOutX = FALSE; /* disable */ + + /* Reception flow control */ + if (t->c_iflag & IXOFF) + state.fInX = TRUE; /* enable */ + else + state.fInX = FALSE; /* disable */ + + /* XoffLim and XonLim are left at default values */ + + state.XonChar = (t->c_cc[VSTART] ? t->c_cc[VSTART] : 0x11); + state.XoffChar = (t->c_cc[VSTOP] ? t->c_cc[VSTOP] : 0x13); + + /* -------------- Set hardware flow control ------------------ */ + + /* Disable DSR flow control */ + state.fOutxDsrFlow = FALSE; + + /* Some old flavors of Unix automatically enabled hardware flow + control when software flow control was not enabled. Since newer + Unices tend to require explicit setting of hardware flow-control, + this is what we do. */ + + /* RTS/CTS flow control */ + if (t->c_cflag & CRTSCTS) + { /* enable */ + state.fOutxCtsFlow = TRUE; + state.fRtsControl = RTS_CONTROL_HANDSHAKE; + } + else + { /* disable */ + state.fRtsControl = RTS_CONTROL_ENABLE; + state.fOutxCtsFlow = FALSE; + } + + /* + if (t->c_cflag & CRTSXOFF) + state.fRtsControl = RTS_CONTROL_HANDSHAKE; + */ + + /* -------------- DTR ------------------ */ + /* Assert DTR on device open */ + + state.fDtrControl = DTR_CONTROL_ENABLE; + + /* -------------- DSR ------------------ */ + /* Assert DSR at the device? */ + + if (t->c_cflag & CLOCAL) + state.fDsrSensitivity = FALSE; /* no */ + else + state.fDsrSensitivity = TRUE; /* yes */ + + /* -------------- Error handling ------------------ */ + /* Since read/write operations terminate upon error, we + will use ClearCommError() to resume. */ + + state.fAbortOnError = TRUE; + + /* -------------- Set state and exit ------------------ */ + if (memcmp (&ostate, &state, sizeof (state)) != 0) + SetCommState (sh->handle, &state); + + sh->r_binary = ((t->c_iflag & IGNCR) ? 0 : 1); + sh->w_binary = ((t->c_oflag & ONLCR) ? 0 : 1); + + if (dropDTR == TRUE) + EscapeCommFunction (sh->handle, CLRDTR); + else + { + /* FIXME: Sometimes when CLRDTR is set, setting + state.fDtrControl = DTR_CONTROL_ENABLE will fail. This + is a problem since a program might want to change some + parameters while DTR is still down. */ + + EscapeCommFunction (sh->handle, SETDTR); + } + + /* + The following documentation on was taken from "Linux Serial Programming + HOWTO". It explains how MIN (t->c_cc[VMIN] || vmin_) and TIME + (t->c_cc[VTIME] || vtime_) is to be used. + + In non-canonical input processing mode, input is not assembled into + lines and input processing (erase, kill, delete, etc.) does not + occur. Two parameters control the behavior of this mode: c_cc[VTIME] + sets the character timer, and c_cc[VMIN] sets the minimum number of + characters to receive before satisfying the read. + + If MIN > 0 and TIME = 0, MIN sets the number of characters to receive + before the read is satisfied. As TIME is zero, the timer is not used. + + If MIN = 0 and TIME > 0, TIME serves as a timeout value. The read will + be satisfied if a single character is read, or TIME is exceeded (t = + TIME *0.1 s). If TIME is exceeded, no character will be returned. + + If MIN > 0 and TIME > 0, TIME serves as an inter-character timer. The + read will be satisfied if MIN characters are received, or the time + between two characters exceeds TIME. The timer is restarted every time + a character is received and only becomes active after the first + character has been received. + + If MIN = 0 and TIME = 0, read will be satisfied immediately. The + number of characters currently available, or the number of characters + requested will be returned. According to Antonino (see contributions), + you could issue a fcntl(fd, F_SETFL, FNDELAY); before reading to get + the same result. + */ + + if (t->c_lflag & ICANON) + { + sh->vmin_ = MAXDWORD; + sh->vtime_ = 0; + } + else + { + sh->vtime_ = t->c_cc[VTIME] * 100; + sh->vmin_ = t->c_cc[VMIN]; + } + + upslogx(LOG_DEBUG,"vtime %d, vmin %d\n", sh->vtime_, sh->vmin_); + + if (ovmin == sh->vmin_ && ovtime == sh->vtime_) { + errno = EINVAL; + return 0; + } + + memset (&to, 0, sizeof (to)); + + if ((sh->vmin_ > 0) && (sh->vtime_ == 0)) + { + /* Returns immediately with whatever is in buffer on a ReadFile(); + or blocks if nothing found. We will keep calling ReadFile(); until + vmin_ characters are read */ + to.ReadIntervalTimeout = to.ReadTotalTimeoutMultiplier = MAXDWORD; + to.ReadTotalTimeoutConstant = MAXDWORD - 1; + } + else if ((sh->vmin_ == 0) && (sh->vtime_ > 0)) + { + /* set timeoout constant appropriately and we will only try to + read one character in ReadFile() */ + to.ReadTotalTimeoutConstant = sh->vtime_; + to.ReadIntervalTimeout = to.ReadTotalTimeoutMultiplier = MAXDWORD; + } + else if ((sh->vmin_ > 0) && (sh->vtime_ > 0)) + { + /* time applies to the interval time for this case */ + to.ReadIntervalTimeout = sh->vtime_; + } + else if ((sh->vmin_ == 0) && (sh->vtime_ == 0)) + { + /* returns immediately with whatever is in buffer as per + Time-Outs docs in Win32 SDK API docs */ + to.ReadIntervalTimeout = MAXDWORD; + } + + upslogx(LOG_DEBUG,"ReadTotalTimeoutConstant %d, ReadIntervalTimeout %d, ReadTotalTimeoutMultiplier %d", + (int)to.ReadTotalTimeoutConstant, (int)to.ReadIntervalTimeout, (int)to.ReadTotalTimeoutMultiplier); + int res = SetCommTimeouts(sh->handle, &to); + if (!res) + { + upslogx(LOG_ERR,"SetCommTimeout failed"); + errno = EIO; + return -1; + } + + return 0; +} + +/* tcgetattr: POSIX 7.2.1.1 */ +int tcgetattr (serial_handler_t * sh, struct termios *t) +{ + DCB state; + + errno = 0; + + /* Get current Win32 comm state */ + if (GetCommState (sh->handle, &state) == 0) { + errno = EIO; + return -1; + } + + /* for safety */ + memset (t, 0, sizeof (*t)); + + /* -------------- Baud rate ------------------ */ + + switch (state.BaudRate) + { + case 0: + /* FIXME: need to drop DTR */ + t->c_cflag = t->c_ospeed = t->c_ispeed = B0; + break; + case CBR_110: + t->c_cflag = t->c_ospeed = t->c_ispeed = B110; + break; + case CBR_300: + t->c_cflag = t->c_ospeed = t->c_ispeed = B300; + break; + case CBR_600: + t->c_cflag = t->c_ospeed = t->c_ispeed = B600; + break; + case CBR_1200: + t->c_cflag = t->c_ospeed = t->c_ispeed = B1200; + break; + case CBR_2400: + t->c_cflag = t->c_ospeed = t->c_ispeed = B2400; + break; + case CBR_4800: + t->c_cflag = t->c_ospeed = t->c_ispeed = B4800; + break; + case CBR_9600: + t->c_cflag = t->c_ospeed = t->c_ispeed = B9600; + break; + case CBR_19200: + t->c_cflag = t->c_ospeed = t->c_ispeed = B19200; + break; + case CBR_38400: + t->c_cflag = t->c_ospeed = t->c_ispeed = B38400; + break; + case CBR_57600: + t->c_cflag = t->c_ospeed = t->c_ispeed = B57600; + break; + case CBR_115200: + t->c_cflag = t->c_ospeed = t->c_ispeed = B115200; + break; + default: + /* Unsupported baud rate! */ + upslogx(LOG_ERR,"Invalid baud rate %d", (int)state.BaudRate); + errno = EINVAL; + return -1; + } + + /* -------------- Byte size ------------------ */ + + switch (state.ByteSize) + { + case 5: + t->c_cflag |= CS5; + break; + case 6: + t->c_cflag |= CS6; + break; + case 7: + t->c_cflag |= CS7; + break; + case 8: + t->c_cflag |= CS8; + break; + default: + /* Unsupported byte size! */ + upslogx(LOG_ERR,"Invalid byte size %d", state.ByteSize); + errno = EINVAL; + return -1; + } + + /* -------------- Stop bits ------------------ */ + + if (state.StopBits == TWOSTOPBITS) + t->c_cflag |= CSTOPB; + + /* -------------- Parity ------------------ */ + + if (state.Parity == ODDPARITY) + t->c_cflag |= (PARENB | PARODD); + if (state.Parity == EVENPARITY) + t->c_cflag |= PARENB; + + /* -------------- Parity errors ------------------ */ + + /* fParity combines the function of INPCK and NOT IGNPAR */ + if (state.fParity == TRUE) + t->c_iflag |= INPCK; + else + t->c_iflag |= IGNPAR; /* not necessarily! */ + + /* -------------- Software flow control ------------------ */ + + /* transmission flow control */ + if (state.fOutX) + t->c_iflag |= IXON; + + /* reception flow control */ + if (state.fInX) + t->c_iflag |= IXOFF; + + t->c_cc[VSTART] = (state.XonChar ? state.XonChar : 0x11); + t->c_cc[VSTOP] = (state.XoffChar ? state.XoffChar : 0x13); + + /* -------------- Hardware flow control ------------------ */ + /* Some old flavors of Unix automatically enabled hardware flow + control when software flow control was not enabled. Since newer + Unices tend to require explicit setting of hardware flow-control, + this is what we do. */ + + /* Input flow-control */ + if ((state.fRtsControl == RTS_CONTROL_HANDSHAKE) && + (state.fOutxCtsFlow == TRUE)) + t->c_cflag |= CRTSCTS; + /* + if (state.fRtsControl == RTS_CONTROL_HANDSHAKE) + t->c_cflag |= CRTSXOFF; + */ + + /* -------------- CLOCAL --------------- */ + /* DSR is only lead toggled only by CLOCAL. Check it to see if + CLOCAL was called. */ + /* FIXME: If tcsetattr() hasn't been called previously, this may + give a false CLOCAL. */ + + if (state.fDsrSensitivity == FALSE) + t->c_cflag |= CLOCAL; + + /* FIXME: need to handle IGNCR */ +#if 0 + if (!sh->r_binary ()) + t->c_iflag |= IGNCR; +#endif + + if (!sh->w_binary) + t->c_oflag |= ONLCR; + + upslogx (LOG_DEBUG,"vmin_ %d, vtime_ %d", sh->vmin_, sh->vtime_); + if (sh->vmin_ == MAXDWORD) + { + t->c_lflag |= ICANON; + t->c_cc[VTIME] = t->c_cc[VMIN] = 0; + } + else + { + t->c_cc[VTIME] = sh->vtime_ / 100; + t->c_cc[VMIN] = sh->vmin_; + } + + return 0; +} + +/* FIXME no difference between ispeed and ospeed */ +void cfsetispeed(struct termios * t, speed_t speed) +{ + t->c_ispeed = t->c_ospeed = speed; +} +void cfsetospeed(struct termios * t, speed_t speed) +{ + t->c_ispeed = t->c_ospeed = speed; +} +speed_t cfgetispeed(const struct termios *t) +{ + return t->c_ispeed; +} + +speed_t cfgetospeed(const struct termios *t) +{ + return t->c_ospeed; +} + +#endif diff --git a/conf/upsd.conf.sample b/conf/upsd.conf.sample index 72927776ee..ce2e7483c4 100644 --- a/conf/upsd.conf.sample +++ b/conf/upsd.conf.sample @@ -33,6 +33,8 @@ # # This defaults to the localhost listening addresses and port 3493. # In case of IP v4 or v6 disabled kernel, only the available one will be used. +# Note that it is not true for Windows platforms. You shouldn't use IPv6 in +# your configuration files unless you have IPv6 installed. # # You may specify each interface you want upsd to listen on for connections, # optionally with a port number. diff --git a/conf/upsmon.conf.sample.in b/conf/upsmon.conf.sample.in index cc34a027f3..d2133c0525 100644 --- a/conf/upsmon.conf.sample.in +++ b/conf/upsmon.conf.sample.in @@ -107,6 +107,13 @@ MINSUPPLIES 1 # upsmon runs this command when the system needs to be brought down. # # This should work just about everywhere ... if it doesn't, well, change it. +# +# For Windows setup use something like: +# SHUTDOWNCMD "C:\\WINDOWS\\system32\\shutdown.exe -s -t 0" +# If you have command line using space character you have to add double quote to them, like this: +# SHUTDOWNCMD "\"C:\\Program Files\\some command.bat\" -first_arg -second_arg" +# Or use the old DOS 8.3 file name, like this: +# SHUTDOWNCMD "C:\\PROGRA~1\\SOMECO~1.bat -first_arg -second_arg" SHUTDOWNCMD "/sbin/shutdown -h +0" @@ -194,6 +201,9 @@ DEADTIME 15 # to shut down the load. You should check for this file's existence in # your shutdown scripts and run 'upsdrvctl shutdown' if it exists. # +# For Windows setup use something like: +# POWERDOWNFLAG "C:\\killpower" +# # See the shutdown.txt file in the docs subdirectory for more information. POWERDOWNFLAG @CONFPATH@/killpower @@ -235,7 +245,9 @@ POWERDOWNFLAG @CONFPATH@/killpower # NOTIFYFLAG - change behavior of upsmon when NOTIFY events occur # # By default, upsmon sends walls (global messages to all logged in users) -# and writes to the syslog when things happen. You can change this. +# and writes to the syslog when things happen. +# Except for Windows where upsmon only writes to the syslog by default. +# You can change this. # # NOTIFYFLAG [+][+] ... # diff --git a/configure.in b/configure.in index 2b6934fcf1..74b46566ef 100644 --- a/configure.in +++ b/configure.in @@ -3,7 +3,7 @@ dnl | Network UPS Tools: configure.in | dnl +------------------------------------------------------------------+ dnl NUT version number is defined here, with a Git suffix in include/nut_version.h -AC_INIT(nut, 2.7.1-pre1) +AC_INIT(nut, 2.6.5-5) AC_CONFIG_SRCDIR(server/upsd.c) AC_CONFIG_MACRO_DIR([m4]) echo "Network UPS Tools version ${PACKAGE_VERSION}" @@ -64,17 +64,28 @@ cgiexecdir='${exec_prefix}/cgi-bin' driverexecdir='${exec_prefix}/bin' htmldir='${prefix}/html' pkgconfigdir='${libdir}/pkgconfig' -hotplugdir='/etc/hotplug' -if test ! -d "${hotplugdir}"; then - hotplugdir='' -fi -udevdir='/lib/udev' -if test ! -d "${udevdir}"; then - udevdir='/etc/udev' - if test ! -d "${udevdir}"; then - udevdir='' - fi -fi + +dnl Disable Hotplug and udev support on Windows +case ${target_os} in + *mingw* ) + LIBREGEX_LIBS='-lregex' + hotplugdir='' + udevdir='' + ;; + * ) + hotplugdir='/etc/hotplug' + if test ! -d "${hotplugdir}"; then + hotplugdir='' + fi + udevdir='/lib/udev' + if test ! -d "${udevdir}"; then + udevdir='/etc/udev' + if test ! -d "${udevdir}"; then + udevdir='' + fi + fi + ;; +esac RUN_AS_USER="nobody" RUN_AS_GROUP="nobody" @@ -103,6 +114,7 @@ AC_CHECK_FUNCS(flock lockf fcvt fcvtl) AC_CHECK_FUNCS(cfsetispeed tcsendbreak) AC_CHECK_FUNCS(seteuid setsid getpassphrase) AC_CHECK_FUNCS(on_exit strptime setlogmask) +AC_CHECK_FUNCS(usleep) AC_CHECK_DECLS(LOG_UPTO, [], [], [#include ]) dnl the following may add stuff to LIBOBJS (is this still needed?) @@ -191,7 +203,7 @@ AC_CHECK_DECLS(__func__, [], [ AC_DEFINE(__func__, __LINE__, [Replace missing __func__ declaration]) ], [AC_INCLUDES_DEFAULT]) ], [AC_INCLUDES_DEFAULT]) - + dnl Solaris compatibility - check for -lnsl and -lsocket AC_SEARCH_LIBS(gethostbyname, nsl) AC_SEARCH_LIBS(connect, socket) @@ -207,6 +219,7 @@ AC_SEARCH_LIBS([pthread_create], [pthread], dnl ---------------------------------------------------------------------- dnl Check for types and define possible replacements NUT_TYPE_SOCKLEN_T +NUT_CHECK_SOCKETLIB dnl ---------------------------------------------------------------------- dnl check for --with-all (or --without-all, or --with-all=auto) flag @@ -714,6 +727,7 @@ fi dnl ---------------------------------------------------------------------- dnl checks related to --with-dev +AC_LIBTOOL_WIN32_DLL dnl We only init libtool there to allow AC_DISABLE_STATIC AC_PROG_LIBTOOL @@ -728,6 +742,20 @@ NUT_REPORT_FEATURE([build and install the development files], [${nut_with_dev}], [WITH_DEV], [Define to enable development files support]) dnl ---------------------------------------------------------------------- +dnl checks related to MS Windows support (MingW) + +AC_CHECK_TOOL(WINDMC, windmc, none) +AC_CHECK_TOOL(WINDRES, windres, none) + +if test "x$WINDMC" != "xnone" -a "x$WINDRES" != "xnone"; then + nut_have_mingw_resgen="yes" +fi + +AM_CONDITIONAL([HAVE_MINGW_RESGEN], [test "${nut_have_mingw_resgen}" = "yes"]) +dnl Also define a generic AM_CONDITIONAL for general Windows compilation +AM_CONDITIONAL([HAVE_WINDOWS], [test "${nut_have_mingw_resgen}" = "yes"]) + +dnl ---------------------------------------------------------------------- AC_MSG_CHECKING(state path) @@ -1110,9 +1138,8 @@ dnl same for datadir conftemp="${datadir}" eval conftemp=\"${conftemp}\" eval conftemp=\"${conftemp}\" -DATADIR=${conftemp} -AC_DEFINE_UNQUOTED(DATADIR, "${conftemp}", [Default path for data files]) - +NUT_DATADIR=${conftemp} +AC_DEFINE_UNQUOTED(NUT_DATADIR, "${conftemp}", [Default path for data files]) dnl same for bindir conftemp="${bindir}" eval conftemp=\"${conftemp}\" @@ -1131,6 +1158,7 @@ AC_SUBST(OS_NAME) AC_SUBST(TREE_VERSION) AC_SUBST(NUT_NETVERSION) AC_SUBST(LIBSSL_CFLAGS) +AC_SUBST(LIBREGEX_LIBS) AC_SUBST(LIBSSL_LIBS) AC_SUBST(LIBGD_CFLAGS) AC_SUBST(LIBGD_LDFLAGS) @@ -1212,6 +1240,7 @@ AC_OUTPUT([ scripts/augeas/nuthostsconf.aug scripts/augeas/nutupssetconf.aug scripts/avahi/nut.service + scripts/Windows/Makefile scripts/hal/Makefile scripts/hal/ups-nut-device.fdi scripts/hotplug/Makefile diff --git a/docs/download.txt b/docs/download.txt index cc1eaf5a97..068011128c 100644 --- a/docs/download.txt +++ b/docs/download.txt @@ -128,7 +128,7 @@ link:http://pdb.finkproject.org/pdb/package.php/nut[Fink], link:http://trac.macports.org/browser/trunk/dports/sysutils/nut/Portfile[MacPorts] - Windows (complete port, Beta): -link:http://www.networkupstools.org/package/windows/NUT-Installer-2.6.5-3.msi[Windows MSI installer 2.6.5-3] +link:http://www.networkupstools.org/package/windows/NUT-Installer-2.6.5-6.msi[Windows MSI installer 2.6.5-6] Java packages diff --git a/docs/man/Makefile.am b/docs/man/Makefile.am index 6dc58c066e..a5c69801cf 100644 --- a/docs/man/Makefile.am +++ b/docs/man/Makefile.am @@ -577,28 +577,24 @@ if HAVE_ASCIIDOC --attribute localtime=`TZ=UTC date +%H:%M:%S` \ -o $@ $< -### The --destination-dir flag doesn't seem to affect the intermediate .xml file. -### Hence, the copying dance below. +### Prior to Asciidoc ~8.6.8, the --destination-dir flag didn't seem to affect the location of the intermediate .xml file. A2X_MANPAGE_OPTS = --doctype manpage --format manpage \ --attribute mansource="Network UPS Tools" \ --attribute manversion="@PACKAGE_VERSION@" \ - --attribute manmanual="NUT Manual" + --attribute manmanual="NUT Manual" \ + --destination-dir=. .txt.1: - test -f `basename $<` || cp -p $< . - $(A2X) $(A2X_MANPAGE_OPTS) `basename $<` + $(A2X) $(A2X_MANPAGE_OPTS) $< .txt.3: - test -f `basename $<` || cp -p $< . - $(A2X) $(A2X_MANPAGE_OPTS) `basename $<` + $(A2X) $(A2X_MANPAGE_OPTS) $< .txt.5: - test -f `basename $<` || cp -p $< . - $(A2X) $(A2X_MANPAGE_OPTS) `basename $<` + $(A2X) $(A2X_MANPAGE_OPTS) $< .txt.8: - test -f `basename $<` || cp -p $< . - $(A2X) $(A2X_MANPAGE_OPTS) `basename $<` + $(A2X) $(A2X_MANPAGE_OPTS) $< else !HAVE_ASCIIDOC diff --git a/docs/man/blazer.txt b/docs/man/blazer.txt new file mode 100644 index 0000000000..c4387a1e4a --- /dev/null +++ b/docs/man/blazer.txt @@ -0,0 +1,310 @@ +BLAZER(8) +========= + +NAME +---- + +blazer, blazer_ser, blazer_usb - Driver for Megatec/Q1 protocol serial and USB based UPS equipment + +NOTE +---- +This man page only documents the hardware-specific features of the +blazer driver. For information about the core driver, see +linkman:nutupsdrv[8]. + +SUPPORTED HARDWARE +------------------ + +The blazer driver is known to work with various UPSes from Blazer, Energy +Sistem, Fenton Technologies, General Electric, Mustek and many others. +The NUT compatibility table lists all the known supported models. Keep +in mind, however, that other models not listed there may also be supported, +but haven't been tested. + +All devices with a serial interface (use the *blazer_ser* driver) and +many with a USB interface (use the *blazer_usb* driver) are supported. + +EXTRA ARGUMENTS +--------------- + +You may need to override or provide defaults for some values, depending on +the make and model of your UPS. The following are the ones that most likely +will need changing (see linkman:ups.conf[5]): + +*default.battery.voltage.high =* 'value':: + +Maximum battery voltage that is reached after about 12 to 24 hours charging. +If you want the driver to report a guesstimated *battery.charge*, you need +to specify this (see <<_battery_charge,BATTERY CHARGE>>). + +*default.battery.voltage.low =* 'value':: + +Minimum battery voltage just before the UPS automatically shuts down. +If you want the driver to report a guesstimated *battery.charge*, you need +to specify this (see <<_battery_charge,BATTERY CHARGE>>). + +*default.battery.voltage.nominal =* 'value':: +*override.battery.voltage.nominal =* 'value':: + +Some devices show a wrong nominal battery voltage (or none at all), so you may +need to override or set a default value. + +*override.battery.packs =* 'value':: + +Some devices report a part of the total battery voltage. For instance, if +*battery.voltage.nominal* is 24 V, but it reports a *battery.voltage* +of around 2 V, the number of *battery.packs* to correct this reading would +be 12. The driver will attempt to detect this automatically, but if this fails +somehow, you may want to override this value. + +*ondelay =* 'value':: + +Time to wait before switching on the UPS (minutes). Note that a value below 3 +minutes, may cause earlier firmware versions to not switch on automatically, +so it defaults to 3 minutes. + +*offdelay =* 'value':: + +Time to wait before shutting down the UPS (seconds). This value is truncated +to units of 6 seconds (less than 60 seconds) or 60 seconds (more than 60 +seconds). Defaults to 30 seconds. + +*norating*:: + +Some UPSes will lock up if you attempt to read rating information from them. +Setting this flag will make the driver skip this step. + +*novendor*:: + +Some UPSes will lock up if you attempt to read vendor information from them. +Setting this flag will make the driver skip this step. + +*protocol =* 'string':: + +Skip autodetection of the protocol to use and only use the one specified. +Supported values 'megatec', 'megatec/old', 'mustek' and 'zinto'. + +*runtimecal =* 'value,value,value,value':: + +Parameter used in the (optional) runtime estimation. This takes two runtimes +at different loads. Typically, this uses the runtime at full load and the +runtime at half load. For instance, if your UPS has a rated runtime of 240 +seconds at full load and 720 seconds at half load, you would enter ++ + runtimecal = 240,100,720,50 ++ +The first load should always be higher than the second. If you have values +available for loads other than 100 and 50 % respectively, you can use those +too, but keep them spaced apart as far as reasonably possible. Just don't +get too close to no load (prediction of runtime depends more on idle load for +the battery then). + +*chargetime =* 'value':: + +The time needed to fully recharge the battery after being fully discharged. If +not specified, the driver defaults to 43200 seconds (12 hours). Only used if +*runtimecal* is also specified. + +*idleload =* 'value':: + +Minimum battery load used by the driver to estimate the runtime. If not +specified, the driver defaults to 10%. Only used if *runtimecal* is also +specified. + +SERIAL INTERFACE ONLY +--------------------- + +*cablepower =* 'string':: + +By default the driver will set DTR and clear RTS ('normal'). If you find that +your UPS isn't detected or the communication with the UPS is unreliable, you may +try if clear DTR and set RTS ('reverse'), set DTR and RTS ('both') or +clear DTR and RTS ('none') improves this situation. + +USB INTERFACE ONLY +------------------ + +*vendorid =* 'regex':: +*productid =* 'regex':: +*vendor =* 'regex':: +*product =* 'regex':: +*serial =* 'regex':: + +Select a specific UPS, in case there is more than one connected via +USB. Each option specifies an extended regular expression (see +*regex(7)*) that must match the UPS's entire vendor/product/serial +string (minus any surrounding whitespace), or the whole 4-digit +hexadecimal code for vendorid and productid. Try *-DD* for +finding out the strings to match. + +Examples: + + -x vendor="Foo.Corporation.*" + + -x vendorid=051d # (APC) + + -x product=".*(Smart|Back)-?UPS.*" + +*bus =* 'regex':: + +Select a UPS on a specific USB bus or group of busses. The argument is +a regular expression that must match the bus name where the UPS is +connected (e.g. bus="002", bus="00[2-3]"). + +*subdriver =* 'string':: + +Select a serial-over-USB subdriver to use. You have a choice between *phoenix*, +*ippon*, *cypress*, and *krauler*. When using this option, it is mandatory to also +specify the *vendorid* and *productid*. + +*langid_fix =* 'value':: + +Apply the language ID workaround to the krauler subdriver. This is mandatory +for some devices to work (LDLC, Dynamix and others). You must to provide +*value* (0x409 or 0x4095), according to your device entry in NUT hardware +compatibility list (HCL). + + +UPS COMMANDS +------------ + +This driver supports some instant commands (see linkman:upscmd[8]): + +*beeper.toggle*:: + +Toggle the UPS beeper. (Not available on some hardware.) + +*load.on*:: + +Turn on the load immediately. + +*load.off*:: + +Turn off the load immediately (see <<_known_problems,KNOWN PROBLEMS>>). + +*shutdown.return* ['value']:: + +Turn off the load and return when power is back. Uses the timers defined by +*ondelay* and *offdelay*. + +*shutdown.stayoff* ['value']:: + +Turn off the load and remain off (see <<_known_problems,KNOWN PROBLEMS>>). Uses +the timer defined by *offdelay*. + +*shutdown.stop*:: + +Stop a shutdown in progress. + +*test.battery.start.deep*:: + +Perform a long battery test (Not available on some hardware.) + +*test.battery.start.quick*:: + +Perform a (10 second) battery test. + +*test.battery.start* 'value':: + +Perform a battery test for the duration of 'value' seconds (truncated to units of +60 seconds). + +*test.battery.stop*:: + +Stop a running battery test (not available on some hardware.) + +BATTERY CHARGE +-------------- + +Due to popular demand, this driver will report a guesstimated *battery.charge* +and optionally *battery.runtime*, provided you specified a couple of the +<<_extra_arguments,EXTRA ARGUMENTS>> listed above. + +If you specify both *battery.voltage.high* and *battery.voltage.low* in +linkman:ups.conf[5], but don't enter *runtimecal*, it will guesstimate the state +of charge by looking at the battery voltage alone. This is not reliable under load, +as this only gives reasonably accurate readings if you disconnect the load, let the +battery rest for a couple of minutes and then measure the open cell voltage. This +just isn't practical if the power went out and the UPS is providing power for your +systems. + + battery.voltage - battery.voltage.low +battery.charge = ------------------------------------------ x 100 % + battery.voltage.high - battery.voltage.low + +There is a way to get better readings without disconnecting the load but this +requires one to keep track on how much (and how fast) current is going in- and +out of the battery. If you specified the *runtimecal*, the driver will attempt +to do this. Note however, that this heavily relies on the values you enter and +that the UPS must be able to report the load as well. There are quite a couple +of devices that report 0 % (or any other fixed value) at all times, in which +case this obviously doesn't work. + +The driver also has no way of determining the degradation of the battery capacity +over time, so you'll have to deal with this yourself (by adjusting the values +in *runtimecal*). Also note that the driver guesses the initial state of charge +based on the battery voltage, so this may be less than 100 %, even when you are +certain that they are full. There is just no way to reliably measure this between +0 and 100 % full charge. + +This is better than nothing (but not by much). If any of the above calculations is +giving you incorrect readings, you are the one that put in the values in +linkman:ups.conf[5], so don't complain with the author. If you need something better, +buy a UPS that reports *battery.charge* and *battery.runtime* all by itself +without the help of a NUT driver. + +NOTES FOR THE PREVIOUS USER OF MEGATEC DRIVERS +---------------------------------------------- + +The blazer drivers having replaced the megatec ones, some configuration +changes may be required by users switching to blazer. + +Part of this, the following megatec options, in ups.conf, have to be changed: + +*battvolts*:: + +You need to use 'default.battery.voltage.high' and 'default.battery.voltage.low' + +*dtr and rts*:: + +You need to use 'cablepower' + +*ignoreoff*:: + +This parameter can simply be discarded, since it was a wrong understanding +of the specification. + +KNOWN PROBLEMS +-------------- + +Some UPS commands aren't supported by all models. In most cases, the driver +will send a message to the system log when the user tries to execute an +unsupported command. Unfortunately, some models don't even provide a way for +the driver to check for this, so the unsupported commands will silently +fail. + +Both the *load.off* and *shutdown.stayoff* instant commands are meant to +turn the load off indefinitely. However, some UPS models don't allow this. + +Some models report a bogus value for the beeper status (will always be 'enabled' +or 'disabled'). So, the *beeper.toggle* command may appear to have no effect +in the status reported by the driver when, in fact, it is working fine. + +The temperature and load value is known to be bogus in some models. + +AUTHORS +------- + +Arjen de Korte , +Alexander Gordeev + +SEE ALSO +-------- + +linkman:nutupsdrv[8], linkman:upsc[8], linkman:upscmd[8], linkman:upsrw[8] + +Internet Resources: +~~~~~~~~~~~~~~~~~~~ + +The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ +The NUT HCL: http://www.networkupstools.org/stable-hcl.html diff --git a/docs/man/upsmon.conf.txt b/docs/man/upsmon.conf.txt index 6117d91d57..caaaf79f5a 100644 --- a/docs/man/upsmon.conf.txt +++ b/docs/man/upsmon.conf.txt @@ -216,8 +216,9 @@ NOCOMM;; A UPS is unavailable (can't be contacted for monitoring) *NOTIFYFLAG* 'type' 'flag'[\+'flag'][+'flag']...:: By default, upsmon sends walls global messages to all logged in users) -via /bin/wall and writes to the syslog when things happen. You can -change this. +via /bin/wall and writes to the syslog when things happen. +Except for Windows where upsmon only writes to the syslog by default. +You can change this. + Examples: + diff --git a/docs/man/upsmon.txt b/docs/man/upsmon.txt index 077144dea0..ba6fdfc9ad 100644 --- a/docs/man/upsmon.txt +++ b/docs/man/upsmon.txt @@ -169,8 +169,9 @@ NOTIFY FLAGS ------------ By default, all notify events (see above) generate a global message -(wall) to all users, plus they are logged via the syslog. You can change -this with the NOTIFYFLAG directive in the configuration file: +(wall) to all users, plus they are logged via the syslog. +Except for Windows where upsmon only writes to the syslog by default. +You can change this with the NOTIFYFLAG directive in the configuration file: +NOTIFYFLAG+ 'notifytype' 'flags' diff --git a/drivers/.gitignore b/drivers/.gitignore index d63a5d8024..d01136a38d 100644 --- a/drivers/.gitignore +++ b/drivers/.gitignore @@ -77,3 +77,4 @@ riello_ser riello_usb voltronic_ser voltronic_usb +*.exe diff --git a/drivers/Makefile.am b/drivers/Makefile.am index 0f1d0c9ef7..182f5a00c6 100644 --- a/drivers/Makefile.am +++ b/drivers/Makefile.am @@ -99,6 +99,7 @@ upsdrvctl_LDADD = $(LDADD_COMMON) # serial drivers: all of them use standard LDADD and CFLAGS apcsmart_SOURCES = apcsmart.c apcsmart_tabs.c +apcsmart_LDADD = $(LDADD) $(LIBREGEX_LIBS) apcsmart_old_SOURCES = apcsmart-old.c bcmxcp_SOURCES = bcmxcp.c bcmxcp_ser.c bcmxcp_LDADD = $(LDADD) -lm @@ -257,7 +258,7 @@ dist_noinst_HEADERS = apc-mib.h apc-hid.h baytech-mib.h bcmxcp.h \ safenet.h serial.h snmp-ups.h solis.h tripplite.h tripplite-hid.h \ upshandler.h usb-common.h usbhid-ups.h powercom-hid.h compaq-mib.h idowell-hid.h \ apcsmart.h apcsmart_tabs.h apcsmart-old.h cyberpower-mib.h riello.h openups-hid.h \ - delta_ups-mib.h voltronic.h + delta_ups-mib.h voltronic.h win_shut_compat.h # Define a dummy library so that Automake builds rules for the # corresponding object files. This library is not actually built, diff --git a/drivers/apcsmart-old.h b/drivers/apcsmart-old.h index f143cfa621..525e3045dd 100644 --- a/drivers/apcsmart-old.h +++ b/drivers/apcsmart-old.h @@ -19,7 +19,9 @@ */ #include +#ifndef WIN32 #include +#endif #include "serial.h" #include "timehead.h" diff --git a/drivers/apcsmart.c b/drivers/apcsmart.c index 018b29dd63..a127867274 100644 --- a/drivers/apcsmart.c +++ b/drivers/apcsmart.c @@ -33,6 +33,12 @@ #include "apcsmart.h" #include "apcsmart_tabs.h" +#ifndef WIN32 +#define INVALID_HANDLE_VALUE -1 +#else +#define ECANCELED ERROR_CANCELLED +#endif + /* driver description structure */ upsdrv_info_t upsdrv_info = { DRIVER_NAME, @@ -441,7 +447,7 @@ static int apc_read_i(char *buf, size_t buflen, int flags, const char *fn, unsig int i, ret, sec = 3, usec = 0; char temp[APC_LBUF]; - if (upsfd == -1) + if (upsfd == ERROR_FD) return 0; if (flags & SER_D0) { sec = 0; usec = 0; @@ -565,8 +571,7 @@ static int apc_write_i(unsigned char code, const char *fn, unsigned int ln) { int ret; errno = 0; - - if (upsfd == -1) + if (upsfd == INVALID_HANDLE_VALUE) return 0; ret = ser_send_char(upsfd, code); diff --git a/drivers/apcsmart.h b/drivers/apcsmart.h index c0155db4d3..27bf7a77dd 100644 --- a/drivers/apcsmart.h +++ b/drivers/apcsmart.h @@ -70,6 +70,10 @@ * we have to ignore this character explicitly */ +#ifndef WIN32 +#include +#endif + /* Basic UPS reply line structure */ #define ENDCHAR 10 /* APC ends responses with LF (and CR, but it's IGNCRed) */ diff --git a/drivers/bcmxcp_usb.c b/drivers/bcmxcp_usb.c index 2788029676..e64cd614bf 100644 --- a/drivers/bcmxcp_usb.c +++ b/drivers/bcmxcp_usb.c @@ -417,6 +417,13 @@ usb_dev_handle *nutusb_open(const char *port) upsdebugx(1, "device %s opened successfully", usb_device(dev_h)->filename); errout = 0; +#ifdef WIN32 + if (usb_set_configuration(dev_h, 1) < 0) + { + upsdebugx(1, "Can't set POWERWARE USB configuration: %s", usb_strerror()); + errout = 1; + } +#endif if (usb_claim_interface(dev_h, 0) < 0) { upsdebugx(1, "Can't claim POWERWARE USB interface: %s", usb_strerror()); diff --git a/drivers/belkin.h b/drivers/belkin.h index 4e8e8d8f40..7a91c07eb7 100644 --- a/drivers/belkin.h +++ b/drivers/belkin.h @@ -17,7 +17,9 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#ifndef WIN32 #include +#endif #include "serial.h" #include "timehead.h" diff --git a/drivers/belkinunv.c b/drivers/belkinunv.c index c5abda2fe7..e1e4e0df03 100644 --- a/drivers/belkinunv.c +++ b/drivers/belkinunv.c @@ -440,17 +440,19 @@ static int belkin_nut_write_int(int reg, int val) { will be discarded. After this call, the device is ready for reading and writing via read(2) and write(2). Return a valid file descriptor on success, or else -1 with errno set. */ -static int belkin_std_open_tty(const char *device) { - int fd; +static TYPE_FD belkin_std_open_tty(const char *device) { + TYPE_FD fd; struct termios tios; +#ifndef WIN32 struct flock flock; +#endif char buf[128]; int r; /* open the device */ fd = open(device, O_RDWR | O_NONBLOCK); - if (fd == -1) { - return -1; + if (fd == ERROR_FD) { + return ERROR_FD; } /* set communications parameters: 2400 baud, 8 bits, 1 stop bit, no @@ -463,7 +465,7 @@ static int belkin_std_open_tty(const char *device) { r = tcsetattr(fd, TCSANOW, &tios); if (r == -1) { close(fd); - return -1; + return ERROR_FD; } /* signal the UPS to enter "smart" mode. This is done by setting RTS @@ -480,17 +482,20 @@ static int belkin_std_open_tty(const char *device) { r = ser_flush_io(fd); if (r == -1) { close(fd); - return -1; + return ERROR_FD; } - + +/* TODO: port to WIN32 */ +#ifndef WIN32 /* lock the port */ memset(&flock, 0, sizeof(flock)); flock.l_type = F_RDLCK; r = fcntl(fd, F_SETLK, &flock); if (r == -1) { close(fd); - return -1; + return ERROR_FD; } +#endif /* sleep at least 0.25 seconds for the UPS to wake up. Belkin's own software sleeps 1 second, so that's what we do, too. */ @@ -501,13 +506,19 @@ static int belkin_std_open_tty(const char *device) { r = tcflush(fd, TCIFLUSH); if (r == -1) { close(fd); - return -1; + return ERROR_FD; } +#ifndef WIN32 r = read(fd, buf, 127); +#else +/* WIN32 : w32_serial_read is blocking, using select_read with 0ms timeout +is non-blocking */ + r = select_read(fd, buf, 127,0,0); +#endif if (r == -1 && errno != EAGAIN) { close(fd); - return -1; + return ERROR_FD; } /* leave port in non-blocking state */ @@ -516,13 +527,19 @@ static int belkin_std_open_tty(const char *device) { } /* blocking read with 1-second timeout (use non-blocking i/o) */ -static int belkin_std_upsread(int fd, unsigned char *buf, int n) { +static int belkin_std_upsread(TYPE_FD fd, unsigned char *buf, int n) { int count = 0; int r; int tries = 0; while (count < n) { +#ifndef WIN32 r = read(fd, &buf[count], n-count); +#else + /* WIN32 : w32_serial_read is blocking, using select_read + with 0ms timeout is non-blocking */ + r = select_read(fd, buf, 127,0,0); +#endif if (r==-1 && errno==EAGAIN) { /* non-blocking i/o, no data available */ usleep(100000); @@ -540,7 +557,7 @@ static int belkin_std_upsread(int fd, unsigned char *buf, int n) { } /* blocking write with 1-second timeout (use non-blocking i/o) */ -static int belkin_std_upswrite(int fd, unsigned char *buf, int n) { +static int belkin_std_upswrite(TYPE_FD fd, unsigned char *buf, int n) { int count = 0; int r; int tries = 0; @@ -566,7 +583,7 @@ static int belkin_std_upswrite(int fd, unsigned char *buf, int n) { /* receive Belkin message from UPS, check for well-formedness (leading byte, checksum). Return length of message, or -1 if not well-formed */ -static int belkin_std_receive(int fd, unsigned char *buf, int bufsize) { +static int belkin_std_receive(TYPE_FD fd, unsigned char *buf, int bufsize) { int r; int n=0; int len; @@ -612,7 +629,7 @@ static int belkin_std_receive(int fd, unsigned char *buf, int bufsize) { /* read the value of an integer register from UPS. Return -1 on failure. */ -static int belkin_std_read_int(int fd, int reg) { +static int belkin_std_read_int(TYPE_FD fd, int reg) { unsigned char buf[MAXMSGSIZE]; int len, r; @@ -654,7 +671,7 @@ static int belkin_std_read_int(int fd, int reg) { /* write the value of an integer register to UPS. Return -1 on failure, else 0 */ -static int belkin_std_write_int(int fd, int reg, int val) { +static int belkin_std_write_int(TYPE_FD fd, int reg, int val) { unsigned char buf[MAXMSGSIZE]; int r; @@ -747,7 +764,7 @@ static int belkin_wait(void) char *val; int failcount = 0; /* count consecutive failed connection attempts */ int failerrno = 0; - int fd; + TYPE_FD fd; int r; int bs, ov, bl, st; @@ -773,7 +790,7 @@ static int belkin_wait(void) updatestatus(smode, "Connecting to UPS..."); failcount = 0; - fd = -1; + fd = ERROR_FD; while (1) { if (failcount >= 3 && nohang) { @@ -783,10 +800,10 @@ static int belkin_wait(void) } else if (failcount >= 3) { updatestatus(smode, "UPS is not responding, will keep trying: %s", strerror(failerrno)); } - if (fd == -1) { + if (fd == ERROR_FD) { fd = belkin_std_open_tty(device_path); } - if (fd == -1) { + if (fd == ERROR_FD) { failcount++; failerrno = errno; sleep(1); @@ -800,7 +817,7 @@ static int belkin_wait(void) failcount++; failerrno = errno; close(fd); - fd = -1; + fd = ERROR_FD; sleep(1); continue; } @@ -809,7 +826,7 @@ static int belkin_wait(void) failcount++; failerrno = errno; close(fd); - fd = -1; + fd = ERROR_FD; sleep(1); continue; } @@ -818,7 +835,7 @@ static int belkin_wait(void) failcount++; failerrno = errno; close(fd); - fd = -1; + fd = ERROR_FD; sleep(1); continue; } diff --git a/drivers/bestfortress.c b/drivers/bestfortress.c index b12dd15975..c89cfcb34d 100644 --- a/drivers/bestfortress.c +++ b/drivers/bestfortress.c @@ -172,7 +172,14 @@ static int upssend(const char *fmt,...) { upslogx(LOG_WARNING, "ser_send_pace: vsnprintf needed more " "than %d bytes", (int)sizeof(buf)); for (p = buf; *p; p++) { +#ifndef WIN32 if (write(upsfd, p, 1) != 1) +#else + DWORD bytes_written; + BOOL res; + res = WriteFile(upsfd, p, 1, &bytes_written,NULL); + if (res == 0 || bytes_written == 0) +#endif return -1; if (d_usec) diff --git a/drivers/blazer_ser.c b/drivers/blazer_ser.c index 5c757261e0..a45da52ff5 100644 --- a/drivers/blazer_ser.c +++ b/drivers/blazer_ser.c @@ -131,6 +131,7 @@ void upsdrv_initups(void) const char *val; +#ifndef WIN32 /* TODO : Correctly set the port parameters for WIN32 */ struct termios tio; /* @@ -181,7 +182,8 @@ void upsdrv_initups(void) * Allow some time to settle for the cablepower */ usleep(100000); -#endif +#endif /* WIN32 */ +#endif /* TESTING */ blazer_initups(); } diff --git a/drivers/blazer_usb.c b/drivers/blazer_usb.c index e3ef3fce08..88a51555b5 100644 --- a/drivers/blazer_usb.c +++ b/drivers/blazer_usb.c @@ -26,6 +26,9 @@ #include "libusb.h" #include "usb-common.h" #include "blazer.h" +#ifdef WIN32 +#include "wincompat.h" +#endif #define DRIVER_NAME "Megatec/Q1 protocol USB driver" #define DRIVER_VERSION "0.10" @@ -442,8 +445,12 @@ int blazer_command(const char *cmd, char *buf, size_t buflen) break; case -ETIMEDOUT: /* Connection timed out */ +/* libusb win32 does not know EPROTO and EOVERFLOW, it only returns EIO for any + IO errors */ +#ifndef WIN32 case -EOVERFLOW: /* Value too large for defined data type */ case -EPROTO: /* Protocol error */ +#endif default: break; } diff --git a/drivers/clone-outlet.c b/drivers/clone-outlet.c index 99d150cf12..14f61dbc32 100644 --- a/drivers/clone-outlet.c +++ b/drivers/clone-outlet.c @@ -22,8 +22,10 @@ #include "parseconf.h" #include +#ifndef WIN32 #include #include +#endif #define DRIVER_NAME "clone outlet UPS Driver" #define DRIVER_VERSION "0.01" @@ -66,6 +68,11 @@ static int dumpdone = 0; static PCONF_CTX_t sock_ctx; static time_t last_heard = 0, last_ping = 0, last_connfail = 0; +#ifdef WIN32 +static char read_buf[SMALLBUF]; +static OVERLAPPED read_overlapped; +#endif + static int parse_args(int numargs, char **arg) { if (numargs < 1) { @@ -140,6 +147,7 @@ static int parse_args(int numargs, char **arg) } +#ifndef WIN32 static int sstate_connect(void) { int ret, fd; @@ -202,30 +210,83 @@ static int sstate_connect(void) return -1; } - pconf_init(&sock_ctx, NULL); +#else +HANDLE sstate_connect(void) +{ + HANDLE fd; + char pipename[SMALLBUF]; + const char *dumpcmd = "DUMPALL\n"; + BOOL result = FALSE; - time(&last_heard); + snprintf(pipename, sizeof(pipename), "\\\\.\\pipe\\%s/%s", dflt_statepath(), device_path); - dumpdone = 0; + result = WaitNamedPipe(pipename,NMPWAIT_USE_DEFAULT_WAIT); - /* set ups.status to "WAIT" while waiting for the driver response to dumpcmd */ - dstate_setinfo("ups.status", "WAIT"); + if( result == FALSE ) { + return INVALID_HANDLE_VALUE; + } - upslogx(LOG_INFO, "Connected to UPS [%s]", device_path); - return fd; -} + fd = CreateFile( + pipename, /* pipe name */ + GENERIC_READ | /* read and write access */ + GENERIC_WRITE, + 0, /* no sharing */ + NULL, /* default security attributes FIXME */ + OPEN_EXISTING, /* opens existing pipe */ + FILE_FLAG_OVERLAPPED, /* enable async IO */ + NULL); /* no template file */ + + if (fd == INVALID_HANDLE_VALUE) { + upslog_with_errno(LOG_ERR, "Can't connect to UPS [%s]", device_path); + return INVALID_HANDLE_VALUE; + } + + /* get a dump started so we have a fresh set of data */ + DWORD bytesWritten = 0; + + result = WriteFile (fd,dumpcmd,strlen(dumpcmd),&bytesWritten,NULL); + if (result == 0 || bytesWritten != strlen(dumpcmd)) { + upslog_with_errno(LOG_ERR, "Initial write to UPS [%s] failed", device_path); + CloseHandle(fd); + return INVALID_HANDLE_VALUE; + } + + /* Start a read IO so we could wait on the event associated with it */ + ReadFile(fd,read_buf,sizeof(read_buf)-1,NULL,&(read_overlapped)); /*-1 to be sure to have a trailling 0 */ +#endif + pconf_init(&sock_ctx, NULL); + + time(&last_heard); + + dumpdone = 0; + + /* set ups.status to "WAIT" while waiting for the driver response to dumpcmd */ + dstate_setinfo("ups.status", "WAIT"); + + upslogx(LOG_INFO, "Connected to UPS [%s]", device_path); + return fd; + } static void sstate_disconnect(void) { +#ifndef WIN32 if (upsfd < 0) { +#else + if (upsfd == INVALID_HANDLE_VALUE) { +#endif return; } pconf_finish(&sock_ctx); +#ifndef WIN32 close(upsfd); upsfd = -1; +#else + CloseHandle(upsfd); + upsfd = INVALID_HANDLE_VALUE; +#endif } @@ -233,11 +294,29 @@ static int sstate_sendline(const char *buf) { int ret; +#ifndef WIN32 if (upsfd < 0) { +#else + if (upsfd == INVALID_HANDLE_VALUE) { +#endif return -1; /* failed */ } +#ifndef WIN32 ret = write(upsfd, buf, strlen(buf)); +#else + DWORD bytesWritten = 0; + BOOL result = FALSE; + + result = WriteFile (upsfd,buf,strlen(buf),&bytesWritten,NULL); + + if( result == 0 ) { + ret = 0; + } + else { + ret = (int)bytesWritten; + } +#endif if (ret == (int)strlen(buf)) { return 0; @@ -251,6 +330,7 @@ static int sstate_sendline(const char *buf) static int sstate_readline(void) { int i, ret; +#ifndef WIN32 char buf[SMALLBUF]; if (upsfd < 0) { @@ -262,33 +342,42 @@ static int sstate_readline(void) if (ret < 0) { switch(errno) { - case EINTR: - case EAGAIN: - return 0; + case EINTR: + case EAGAIN: + return 0; - default: - upslog_with_errno(LOG_WARNING, "Read from UPS [%s] failed", device_path); - return -1; + default: + upslog_with_errno(LOG_WARNING, "Read from UPS [%s] failed", device_path); + return -1; } } +#else + if (upsfd == INVALID_HANDLE_VALUE) { + return -1; + } + char *buf = read_buf; + DWORD bytesRead; + GetOverlappedResult(upsfd, &read_overlapped, &bytesRead, FALSE); + ret = bytesRead; +#endif for (i = 0; i < ret; i++) { switch (pconf_char(&sock_ctx, buf[i])) { - case 1: - if (parse_args(sock_ctx.numargs, sock_ctx.arglist)) { - time(&last_heard); - } - continue; - - case 0: - continue; /* haven't gotten a line yet */ - - default: - /* parse error */ - upslogx(LOG_NOTICE, "Parse error on sock: %s", sock_ctx.errmsg); - return -1; + case 1: + if (parse_args(sock_ctx.numargs, sock_ctx.arglist)) { + time(&last_heard); + } + continue; + + case 0: + continue; /* haven't gotten a line yet */ + + default: + /* parse error */ + upslogx(LOG_NOTICE, "Parse error on sock: %s", sock_ctx.errmsg); + return -1; } } diff --git a/drivers/clone.c b/drivers/clone.c index c8beb830dc..151092988b 100644 --- a/drivers/clone.c +++ b/drivers/clone.c @@ -22,8 +22,10 @@ #include "parseconf.h" #include +#ifndef WIN32 #include #include +#endif #define DRIVER_NAME "Clone UPS driver" #define DRIVER_VERSION "0.02" @@ -61,7 +63,13 @@ static int offdelay = 120, ondelay = 30; static PCONF_CTX_t sock_ctx; static time_t last_poll = 0, last_heard = 0, - last_ping = 0, last_connfail = 0; + last_ping = 0; +#ifndef WIN32 +static time_t last_connfail = 0; +#else +static char read_buf[SMALLBUF]; +static OVERLAPPED read_overlapped; +#endif static int instcmd(const char *cmdname, const char *extra); @@ -154,6 +162,7 @@ static int parse_args(int numargs, char **arg) } +#ifndef WIN32 static int sstate_connect(void) { int ret, fd; @@ -216,30 +225,84 @@ static int sstate_connect(void) return -1; } - pconf_init(&sock_ctx, NULL); +#else +HANDLE sstate_connect(void) +{ + HANDLE fd; + char pipename[SMALLBUF]; + const char *dumpcmd = "DUMPALL\n"; + BOOL result = FALSE; - time(&last_heard); + snprintf(pipename, sizeof(pipename), "\\\\.\\pipe\\%s/%s", dflt_statepath(), device_path); - dumpdone = 0; + result = WaitNamedPipe(pipename,NMPWAIT_USE_DEFAULT_WAIT); - /* set ups.status to "WAIT" while waiting for the driver response to dumpcmd */ - dstate_setinfo("ups.status", "WAIT"); + if( result == FALSE ) { + return INVALID_HANDLE_VALUE; + } - upslogx(LOG_INFO, "Connected to UPS [%s]", device_path); - return fd; -} + fd = CreateFile( + pipename, /* pipe name */ + GENERIC_READ | /* read and write access */ + GENERIC_WRITE, + 0, /* no sharing */ + NULL, /* default security attributes FIXME */ + OPEN_EXISTING, /* opens existing pipe */ + FILE_FLAG_OVERLAPPED, /* enable async IO */ + NULL); /* no template file */ + + if (fd == INVALID_HANDLE_VALUE) { + upslog_with_errno(LOG_ERR, "Can't connect to UPS [%s]", device_path); + return INVALID_HANDLE_VALUE; + } + + /* get a dump started so we have a fresh set of data */ + DWORD bytesWritten = 0; + + result = WriteFile (fd,dumpcmd,strlen(dumpcmd),&bytesWritten,NULL); + if (result == 0 || bytesWritten != strlen(dumpcmd)) { + upslog_with_errno(LOG_ERR, "Initial write to UPS [%s] failed", device_path); + CloseHandle(fd); + return INVALID_HANDLE_VALUE; + } + + /* Start a read IO so we could wait on the event associated with it */ + ReadFile(fd,read_buf,sizeof(read_buf)-1,NULL,&(read_overlapped)); /*-1 to be sure to have a trailling 0 */ +#endif + + pconf_init(&sock_ctx, NULL); + + time(&last_heard); + + dumpdone = 0; + + /* set ups.status to "WAIT" while waiting for the driver response to dumpcmd */ + dstate_setinfo("ups.status", "WAIT"); + + upslogx(LOG_INFO, "Connected to UPS [%s]", device_path); + return fd; + } static void sstate_disconnect(void) { +#ifndef WIN32 if (upsfd < 0) { +#else + if (upsfd == INVALID_HANDLE_VALUE) { +#endif return; } pconf_finish(&sock_ctx); +#ifndef WIN32 close(upsfd); upsfd = -1; +#else + CloseHandle(upsfd); + upsfd = INVALID_HANDLE_VALUE; +#endif } @@ -247,11 +310,29 @@ static int sstate_sendline(const char *buf) { int ret; +#ifndef WIN32 if (upsfd < 0) { +#else + if (upsfd == INVALID_HANDLE_VALUE) { +#endif return -1; /* failed */ } +#ifndef WIN32 ret = write(upsfd, buf, strlen(buf)); +#else + DWORD bytesWritten = 0; + BOOL result = FALSE; + + result = WriteFile (upsfd,buf,strlen(buf),&bytesWritten,NULL); + + if( result == 0 ) { + ret = 0; + } + else { + ret = (int)bytesWritten; + } +#endif if (ret == (int)strlen(buf)) { return 0; @@ -265,6 +346,7 @@ static int sstate_sendline(const char *buf) static int sstate_readline(void) { int i, ret; +#ifndef WIN32 char buf[SMALLBUF]; if (upsfd < 0) { @@ -285,6 +367,15 @@ static int sstate_readline(void) return -1; } } +#else + if (upsfd == INVALID_HANDLE_VALUE) { + return -1; + } + char *buf = read_buf; + DWORD bytesRead; + GetOverlappedResult(upsfd, &read_overlapped, &bytesRead, FALSE); + ret = bytesRead; +#endif for (i = 0; i < ret; i++) { diff --git a/drivers/dstate.c b/drivers/dstate.c index 2e5bd7e967..d7cefd9b17 100644 --- a/drivers/dstate.c +++ b/drivers/dstate.c @@ -21,20 +21,33 @@ */ #include +#ifndef WIN32 #include #include #include #include #include #include +#else +#include +#include +#include "wincompat.h" +#endif #include "common.h" #include "dstate.h" #include "state.h" #include "parseconf.h" - static int sockfd = -1, stale = 1, alarm_active = 0, ignorelb = 0; +#ifndef WIN32 + static int sockfd = -1; static char *sockfn = NULL; +#else + static HANDLE sockfd = INVALID_HANDLE_VALUE; + static OVERLAPPED connect_overlapped; + static char *pipename = NULL; +#endif + static int stale = 1, alarm_active = 0, ignorelb = 0; static char status_buf[ST_MAX_VALUE_LEN], alarm_buf[ST_MAX_VALUE_LEN]; static st_tree_t *dtree_root = NULL; static conn_t *connhead = NULL; @@ -42,6 +55,7 @@ struct ups_handler upsh; +#ifndef WIN32 /* this may be a frequent stumbling point for new users, so be verbose here */ static void sock_fail(const char *fn) { @@ -136,13 +150,59 @@ static int sock_open(const char *fn) if (ret < 0) { fatal_with_errno(EXIT_FAILURE, "listen(%d, %d) failed", fd, DS_LISTEN_BACKLOG); } +#else +static HANDLE sock_open(const char *fn) +{ + HANDLE fd; + + fd = CreateNamedPipe( + fn, // pipe name + PIPE_ACCESS_DUPLEX | // read/write access + FILE_FLAG_OVERLAPPED, // async IO + PIPE_TYPE_BYTE | + PIPE_READMODE_BYTE | + PIPE_WAIT, + PIPE_UNLIMITED_INSTANCES, // max. instances + ST_SOCK_BUF_LEN, // output buffer size + ST_SOCK_BUF_LEN, // input buffer size + 0, // client time-out + NULL); // FIXME: default security attribute + + if (fd == INVALID_HANDLE_VALUE) { + fatal_with_errno(EXIT_FAILURE, "Can't create a state socket (windows named pipe)"); + } + + /* Prepare an async wait on a connection on the pipe */ + memset(&connect_overlapped,0,sizeof(connect_overlapped)); + connect_overlapped.hEvent = CreateEvent(NULL, /*Security*/ + FALSE, /* auto-reset*/ + FALSE, /* inital state = non signaled*/ + NULL /* no name*/); + if(connect_overlapped.hEvent == NULL ) { + fatal_with_errno(EXIT_FAILURE, "Can't create event"); + } + + /* Wait for a connection */ + ConnectNamedPipe(fd,&connect_overlapped); + +#endif + return fd; } static void sock_disconnect(conn_t *conn) { +#ifndef WIN32 close(conn->fd); +#else + /* FIXME not sure if this is the right way to close a connection */ + if( conn->read_overlapped.hEvent != INVALID_HANDLE_VALUE) { + CloseHandle(conn->read_overlapped.hEvent); + conn->read_overlapped.hEvent = INVALID_HANDLE_VALUE; + } + DisconnectNamedPipe(conn->fd); +#endif pconf_finish(&conn->ctx); @@ -182,10 +242,25 @@ static void send_to_all(const char *fmt, ...) for (conn = connhead; conn; conn = cnext) { cnext = conn->next; +#ifndef WIN32 ret = write(conn->fd, buf, strlen(buf)); +#else + DWORD bytesWritten = 0; + BOOL result = FALSE; + + result = WriteFile (conn->fd,buf,strlen(buf),&bytesWritten,NULL); + if( result == 0 ) { + upsdebugx(2, "write failed on %d, disconnecting", (int)conn->fd); + sock_disconnect(conn); + continue; + } + else { + ret = (int)bytesWritten; + } +#endif if (ret != (int)strlen(buf)) { - upsdebugx(2, "write %d bytes to socket %d failed", (int)strlen(buf), conn->fd); + upsdebugx(2, "write %d bytes to socket %d failed", (int)strlen(buf), (int)conn->fd); sock_disconnect(conn); } } @@ -196,6 +271,10 @@ static int send_to_one(conn_t *conn, const char *fmt, ...) int ret; va_list ap; char buf[ST_SOCK_BUF_LEN]; +#ifdef WIN32 + DWORD bytesWritten = 0; + BOOL result = FALSE; +#endif va_start(ap, fmt); ret = vsnprintf(buf, sizeof(buf), fmt, ap); @@ -208,10 +287,20 @@ static int send_to_one(conn_t *conn, const char *fmt, ...) upsdebugx(5, "%s: %.*s", __func__, ret-1, buf); +#ifndef WIN32 ret = write(conn->fd, buf, strlen(buf)); +#else + result = WriteFile (conn->fd,buf,strlen(buf),&bytesWritten,NULL); + if( result == 0 ) { + ret = 0; + } + else { + ret = (int)bytesWritten; + } +#endif if (ret != (int)strlen(buf)) { - upsdebugx(2, "write %d bytes to socket %d failed", (int)strlen(buf), conn->fd); + upsdebugx(2, "write %d bytes to socket %d failed", (int)strlen(buf), (int)conn->fd); sock_disconnect(conn); return 0; /* failed */ } @@ -219,10 +308,18 @@ static int send_to_one(conn_t *conn, const char *fmt, ...) return 1; /* OK */ } +#ifndef WIN32 static void sock_connect(int sock) +#else +static void sock_connect(HANDLE sock) +#endif { - int fd, ret; conn_t *conn; + +#ifndef WIN32 + int ret; + int fd; + struct sockaddr_un sa; #if defined(__hpux) && !defined(_XOPEN_SOURCE_EXTENDED) int salen; @@ -268,6 +365,69 @@ static void sock_connect(int sock) connhead = conn; upsdebugx(3, "new connection on fd %d", fd); +#else + /* We have detected a connection on the opened pipe. So we start by saving its handle and cretae a new pipe for future connection */ + conn = xcalloc(1, sizeof(*conn)); + conn->fd = sock; + + /* sockfd is the handle of the connection pending pipe */ + sockfd = CreateNamedPipe( + pipename, // pipe name + PIPE_ACCESS_DUPLEX | // read/write access + FILE_FLAG_OVERLAPPED, // async IO + PIPE_TYPE_BYTE | + PIPE_READMODE_BYTE | + PIPE_WAIT, + PIPE_UNLIMITED_INSTANCES, // max. instances + ST_SOCK_BUF_LEN, // output buffer size + ST_SOCK_BUF_LEN, // input buffer size + 0, // client time-out + NULL); // FIXME: default security attribute + + if (sockfd == INVALID_HANDLE_VALUE) { + fatal_with_errno(EXIT_FAILURE, "Can't create a state socket (windows named pipe)"); + } + + /* Prepare a new async wait for a connection on the pipe */ + CloseHandle(connect_overlapped.hEvent); + memset(&connect_overlapped,0,sizeof(connect_overlapped)); + connect_overlapped.hEvent = CreateEvent(NULL, /*Security*/ + FALSE, /* auto-reset*/ + FALSE, /* inital state = non signaled*/ + NULL /* no name*/); + if(connect_overlapped.hEvent == NULL ) { + fatal_with_errno(EXIT_FAILURE, "Can't create event"); + } + + /* Wait for a connection */ + ConnectNamedPipe(sockfd,&connect_overlapped); + + /* A new pipe waiting for new client connection has been created. We could manage the current connection now */ + /* Start a read operation on the newly connected pipe so we could wait on the event associated to this IO */ + memset(&conn->read_overlapped,0,sizeof(conn->read_overlapped)); + memset(conn->buf,0,sizeof(conn->buf)); + conn->read_overlapped.hEvent = CreateEvent(NULL, /*Security*/ + FALSE, /* auto-reset*/ + FALSE, /* inital state = non signaled*/ + NULL /* no name*/); + if(conn->read_overlapped.hEvent == NULL ) { + fatal_with_errno(EXIT_FAILURE, "Can't create event"); + } + + ReadFile (conn->fd,conn->buf,sizeof(conn->buf)-1,NULL,&(conn->read_overlapped)); /* -1 to be sure to have a trailling 0 */ + + pconf_init(&conn->ctx, NULL); + + if (connhead) { + conn->next = connhead; + connhead->prev = conn; + } + + connhead = conn; + + upsdebugx(3, "new connection on fd %d", (int)sock); +#endif + } static int st_tree_dump_conn(st_tree_t *node, conn_t *conn) @@ -429,9 +589,15 @@ static int sock_arg(conn_t *conn, int numarg, char **arg) return 0; } +#ifdef WIN32 +void set_exit_flag(int sig); +#endif + static void sock_read(conn_t *conn) { int i, ret; + +#ifndef WIN32 char buf[SMALLBUF]; ret = read(conn->fd, buf, sizeof(buf)); @@ -448,6 +614,24 @@ static void sock_read(conn_t *conn) return; } } +#else + char *buf = conn->buf; + DWORD bytesRead; + BOOL res; + res = GetOverlappedResult(conn->fd, &conn->read_overlapped, &bytesRead, FALSE); + if( res == 0 ) { + upslogx(LOG_INFO, "Read error : %d",(int)GetLastError()); + sock_disconnect(conn); + return; + } + ret = bytesRead; + + /* Special case for signals */ + if (!strncmp(conn->buf, COMMAND_STOP, sizeof(COMMAND_STOP))) { + set_exit_flag(1); + return; + } +#endif for (i = 0; i < ret; i++) { @@ -473,12 +657,18 @@ static void sock_read(conn_t *conn) return; } } + +#ifdef WIN32 + /* Restart async read */ + memset(conn->buf,0,sizeof(conn->buf)); + ReadFile(conn->fd,conn->buf,sizeof(conn->buf)-1,NULL,&(conn->read_overlapped)); /* -1 to be sure to have a trailling 0 */ +#endif } static void sock_close(void) { conn_t *conn, *cnext; - +#ifndef WIN32 if (sockfd != -1) { close(sockfd); sockfd = -1; @@ -490,6 +680,14 @@ static void sock_close(void) } } +#else + if (sockfd != INVALID_HANDLE_VALUE) { + FlushFileBuffers(sockfd); + CloseHandle(sockfd); + sockfd = INVALID_HANDLE_VALUE; + } +#endif + for (conn = connhead; conn; conn = cnext) { cnext = conn->next; sock_disconnect(conn); @@ -505,6 +703,7 @@ void dstate_init(const char *prog, const char *devname) { char sockname[SMALLBUF]; +#ifndef WIN32 /* do this here for now */ signal(SIGPIPE, SIG_IGN); @@ -513,12 +712,18 @@ void dstate_init(const char *prog, const char *devname) } else { snprintf(sockname, sizeof(sockname), "%s/%s", dflt_statepath(), prog); } +#else + /* upsname (and so devname) is now mandatory so no need to test it */ + snprintf(sockname, sizeof(sockname), "\\\\.\\pipe\\%s-%s", prog, devname); + pipename = strdup(sockname); +#endif sockfd = sock_open(sockname); - upsdebugx(2, "dstate_init: sock %s open on fd %d", sockname, sockfd); + upsdebugx(2, "dstate_init: sock %s open on fd %d", sockname, (int)sockfd); } +#ifndef WIN32 /* returns 1 if timeout expired or data is available on UPS fd, 0 otherwise */ int dstate_poll_fds(struct timeval timeout, int extrafd) { @@ -605,6 +810,98 @@ int dstate_poll_fds(struct timeval timeout, int extrafd) return overrun; } +#else +/* returns 1 if timeout expired or data is available on UPS fd, 0 otherwise */ +int dstate_poll_fds(struct timeval timeout, HANDLE extrafd) +{ + DWORD ret; + int maxfd = 0, overrun = 0; + HANDLE rfds[32]; + conn_t *conn; + struct timeval now; + DWORD timeout_ms; + +/* + if (extrafd != -1) { + rfds[maxfd] = extrafd; + maxfd++; + } +*/ + + gettimeofday(&now, NULL); + + /* number of microseconds should always be positive */ + if (timeout.tv_usec < now.tv_usec) { + timeout.tv_sec -= 1; + timeout.tv_usec += 1000000; + } + + if (timeout.tv_sec < now.tv_sec) { + timeout.tv_sec = 0; + timeout.tv_usec = 0; + overrun = 1; /* no time left */ + } else { + timeout.tv_sec -= now.tv_sec; + timeout.tv_usec -= now.tv_usec; + } + + timeout_ms = (timeout.tv_sec * 1000) + (timeout.tv_usec / 1000); + + /* Wait on the read IO of each connections */ + for (conn = connhead; conn; conn = conn->next) { + rfds[maxfd] = conn->read_overlapped.hEvent; + maxfd++; + } + /* Add the connect event */ + rfds[maxfd] = connect_overlapped.hEvent; + maxfd++; + + ret = WaitForMultipleObjects( + maxfd, /* number of objects in array */ + rfds, /* array of objects */ + FALSE, /* wait for any object */ + timeout_ms); /* timeout in millisecond */ + + if (ret == WAIT_TIMEOUT) { + return 1; /* timer expired */ + } + + if (ret == WAIT_FAILED) { + upslog_with_errno(LOG_ERR, "waitfor failed"); + return overrun; + } + + /* Retrieve the signaled connection */ + for(conn = connhead; conn != NULL; conn = conn->next) { + if( conn->read_overlapped.hEvent == rfds[ret-WAIT_OBJECT_0]) { + break; + } + } + + /* the connection event handle has been signaled */ + if (rfds[ret] == connect_overlapped.hEvent) { + sock_connect(sockfd); + } + /* one of the read event handle has been signaled */ + else { + if( conn != NULL) { + sock_read(conn); + } + } + + /* tell the caller if that fd woke up */ +/* + if ((extrafd != -1) && (ret == extrafd)) { + return 1; + } +*/ + return overrun; +} +#endif + +/****************************************************************** + * COMMON + ******************************************************************/ int dstate_setinfo(const char *var, const char *fmt, ...) { diff --git a/drivers/dstate.h b/drivers/dstate.h index b4a5aa983e..8dfe2e4c00 100644 --- a/drivers/dstate.h +++ b/drivers/dstate.h @@ -33,7 +33,13 @@ /* track client connections */ typedef struct conn_s { +#ifdef WIN32 + HANDLE fd; + char buf[LARGEBUF]; + OVERLAPPED read_overlapped; +#else int fd; +#endif PCONF_CTX_t ctx; struct conn_s *prev; struct conn_s *next; @@ -42,7 +48,11 @@ typedef struct conn_s { extern struct ups_handler upsh; void dstate_init(const char *prog, const char *devname); +#ifndef WIN32 int dstate_poll_fds(struct timeval timeout, int extrafd); +#else +int dstate_poll_fds(struct timeval timeout, HANDLE extrafd); +#endif int dstate_setinfo(const char *var, const char *fmt, ...) __attribute__ ((__format__ (__printf__, 2, 3))); int dstate_addenum(const char *var, const char *fmt, ...) diff --git a/drivers/dummy-ups.c b/drivers/dummy-ups.c index 7dbb9910d2..508e58054d 100644 --- a/drivers/dummy-ups.c +++ b/drivers/dummy-ups.c @@ -29,9 +29,12 @@ * * poll the "port" file for change */ +#ifndef WIN32 #include #include #include +#endif + #include #include "main.h" @@ -67,7 +70,11 @@ time_t next_update = -1; static int setvar(const char *varname, const char *val); static int instcmd(const char *cmdname, const char *extra); +#ifndef WIN32 static int parse_data_file(int upsfd); +#else +static int parse_data_file(HANDLE upsfd); +#endif static dummy_info_t *find_info(const char *varname); static int is_valid_data(const char* varname); static int is_valid_value(const char* varname, const char *value); @@ -418,7 +425,11 @@ static void upsconf_err(const char *errmsg) /* for dummy mode * parse the definition file and process its content */ +#ifndef WIN32 static int parse_data_file(int upsfd) +#else +static int parse_data_file(HANDLE upsfd) +#endif { char fn[SMALLBUF]; char *ptr, var_value[MAX_STRING_SIZE]; diff --git a/drivers/genericups.c b/drivers/genericups.c index bc8ddc43d6..64db77a7a6 100644 --- a/drivers/genericups.c +++ b/drivers/genericups.c @@ -17,7 +17,9 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#ifndef WIN32 #include +#endif #include "main.h" #include "serial.h" @@ -154,9 +156,13 @@ void upsdrv_initinfo(void) /* normal idle loop - keep up with the current state of the UPS */ void upsdrv_updateinfo(void) { - int flags, ol, bl, ret; + int flags, ol, bl, ret; +#ifndef WIN32 ret = ioctl(upsfd, TIOCMGET, &flags); +#else + ret = w32_getcomm( upsfd, &flags ); +#endif if (ret != 0) { upslog_with_errno(LOG_INFO, "ioctl failed"); @@ -241,8 +247,10 @@ void upsdrv_shutdown(void) if (flags == TIOCM_ST) { +#ifndef WIN32 #ifndef HAVE_TCSENDBREAK fatalx(EXIT_FAILURE, "Need to send a BREAK, but don't have tcsendbreak!"); +#endif #endif ret = tcsendbreak(upsfd, 4901); @@ -254,7 +262,11 @@ void upsdrv_shutdown(void) return; } +#ifndef WIN32 ret = ioctl(upsfd, TIOCMSET, &flags); +#else + ret = w32_setcomm(upsfd,&flags); +#endif if (ret != 0) { fatal_with_errno(EXIT_FAILURE, "ioctl TIOCMSET"); @@ -326,7 +338,11 @@ void upsdrv_initups(void) upsdebugx(2, "parse_output_signals: SD overridden with %s\n", v); } +#ifndef WIN32 if (ioctl(upsfd, TIOCMSET, &upstab[upstype].line_norm)) { +#else + if (w32_setcomm(upsfd,&upstab[upstype].line_norm)) { +#endif fatal_with_errno(EXIT_FAILURE, "ioctl TIOCMSET"); } } @@ -335,4 +351,3 @@ void upsdrv_cleanup(void) { ser_close(upsfd, device_path); } - diff --git a/drivers/isbmex.c b/drivers/isbmex.c index 17328b2abe..ef538032e9 100644 --- a/drivers/isbmex.c +++ b/drivers/isbmex.c @@ -147,10 +147,12 @@ void upsdrv_initinfo(void) } static const char *getpacket(int *we_know){ +#ifndef WIN32 fd_set readfds; struct timeval tv; - int bytes_per_packet=0; int ret; +#endif + int bytes_per_packet=0; static const char *packet_id=NULL; static char buf[256]; const char *s; @@ -159,7 +161,8 @@ static const char *getpacket(int *we_know){ bytes_per_packet=*we_know; D(printf("getpacket with %d\n",bytes_per_packet);) - + +#ifndef WIN32 FD_ZERO(&readfds); FD_SET(upsfd,&readfds); /* Wait up to 2 seconds. */ @@ -175,17 +178,33 @@ static const char *getpacket(int *we_know){ } r=read(upsfd,buf,255); +#else + r = select_read(upsfd,buf,255,5,0); + if (r <= 0) { + s="Nothing received from UPS. Check cable conexion"; + upslogx(LOG_ERR, "%s", s); + D(printf("%s\n",s);) + return NULL; + } +#endif D(printf("%d bytes read: ",r);) buf[r]=0; if (bytes_per_packet && r < bytes_per_packet){ ssize_t rr; D(printf("short read...\n");) usleep(500000); +#ifndef WIN32 tv.tv_sec = 2; tv.tv_usec = 0; ret=select(upsfd+1, &readfds, NULL, NULL, &tv); if (!ret) return NULL; rr=read(upsfd,buf+r,255-r); +#else + rr = select_read(upsfd,buf+r,255-r,2,0); + if (rr <= 0) { + return NULL; + } +#endif r += rr; if (r < bytes_per_packet) return NULL; } diff --git a/drivers/libhid.c b/drivers/libhid.c index d0ba7c5761..1f645d8275 100644 --- a/drivers/libhid.c +++ b/drivers/libhid.c @@ -143,7 +143,7 @@ reportbuf_t *new_report_buffer(HIDDesc_t *pDesc) /* because buggy firmwares from APC return wrong report size, we either ask the report with the found report size or with the whole buffer size depending on the max_report_size flag */ -static int refresh_report_buffer(reportbuf_t *rbuf, hid_dev_handle_t udev, HIDData_t *pData, int age) +static int refresh_report_buffer(reportbuf_t *rbuf, TYPE_FD udev, HIDData_t *pData, int age) { int id = pData->ReportID; int r; @@ -178,7 +178,7 @@ static int refresh_report_buffer(reportbuf_t *rbuf, hid_dev_handle_t udev, HIDDa conversion is performed. If age>0, the read operation is buffered if the item's age is less than "age". On success, return 0 and store the answer in *value. On failure, return -1 and set errno. */ -static int get_item_buffered(reportbuf_t *rbuf, hid_dev_handle_t udev, HIDData_t *pData, long *Value, int age) +static int get_item_buffered(reportbuf_t *rbuf, TYPE_FD udev, HIDData_t *pData, long *Value, int age) { int id = pData->ReportID; int r; @@ -196,7 +196,7 @@ static int get_item_buffered(reportbuf_t *rbuf, hid_dev_handle_t udev, HIDData_t /* set the logical value for the given pData. No physical to logical conversion is performed. On success, return 0, and failure, return -1 and set errno. The updated value is sent to the device. */ -static int set_item_buffered(reportbuf_t *rbuf, hid_dev_handle_t udev, HIDData_t *pData, long Value) +static int set_item_buffered(reportbuf_t *rbuf, TYPE_FD udev, HIDData_t *pData, long Value) { int id = pData->ReportID; int r; @@ -266,7 +266,7 @@ static struct { * since it's used to produce sub-drivers "stub" using * scripts/subdriver/gen-usbhid-subdriver.sh */ -void HIDDumpTree(hid_dev_handle_t udev, usage_tables_t *utab) +void HIDDumpTree(TYPE_FD udev, usage_tables_t *utab) { int i; #ifndef SHUT_MODE @@ -362,7 +362,7 @@ char *HIDGetDataItem(const HIDData_t *hiddata, usage_tables_t *utab) /* Return the physical value associated with the given HIDData path. * return 1 if OK, 0 on fail, -errno otherwise (ie disconnect). */ -int HIDGetDataValue(hid_dev_handle_t udev, HIDData_t *hiddata, double *Value, int age) +int HIDGetDataValue(TYPE_FD udev, HIDData_t *hiddata, double *Value, int age) { int r; long hValue; @@ -391,14 +391,14 @@ int HIDGetDataValue(hid_dev_handle_t udev, HIDData_t *hiddata, double *Value, in /* Return the physical value associated with the given path. * return 1 if OK, 0 on fail, -errno otherwise (ie disconnect). */ -int HIDGetItemValue(hid_dev_handle_t udev, const char *hidpath, double *Value, usage_tables_t *utab) +int HIDGetItemValue(TYPE_FD udev, const char *hidpath, double *Value, usage_tables_t *utab) { return HIDGetDataValue(udev, HIDGetItemData(hidpath, utab), Value, MAX_TS); } /* Return pointer to indexed string (empty if not found) */ -char *HIDGetIndexString(hid_dev_handle_t udev, const int Index, char *buf, size_t buflen) +char *HIDGetIndexString(TYPE_FD udev, const int Index, char *buf, size_t buflen) { if (comm_driver->get_string(udev, Index, buf, buflen) < 1) buf[0] = '\0'; @@ -408,7 +408,7 @@ char *HIDGetIndexString(hid_dev_handle_t udev, const int Index, char *buf, size_ /* Return pointer to indexed string from HID path (empty if not found) */ -char *HIDGetItemString(hid_dev_handle_t udev, const char *hidpath, char *buf, size_t buflen, usage_tables_t *utab) +char *HIDGetItemString(TYPE_FD udev, const char *hidpath, char *buf, size_t buflen, usage_tables_t *utab) { double Index; @@ -423,7 +423,7 @@ char *HIDGetItemString(hid_dev_handle_t udev, const char *hidpath, char *buf, si /* Set the given physical value for the variable associated with * path. return 1 if OK, 0 on fail, -errno otherwise (ie disconnect). */ -int HIDSetDataValue(hid_dev_handle_t udev, HIDData_t *hiddata, double Value) +int HIDSetDataValue(TYPE_FD udev, HIDData_t *hiddata, double Value) { int i, r; long hValue; @@ -460,7 +460,7 @@ int HIDSetDataValue(hid_dev_handle_t udev, HIDData_t *hiddata, double Value) return 1; } -bool_t HIDSetItemValue(hid_dev_handle_t udev, const char *hidpath, double Value, usage_tables_t *utab) +bool_t HIDSetItemValue(TYPE_FD udev, const char *hidpath, double Value, usage_tables_t *utab) { if (HIDSetDataValue(udev, HIDGetItemData(hidpath, utab), Value) != 1) return FALSE; @@ -471,7 +471,7 @@ bool_t HIDSetItemValue(hid_dev_handle_t udev, const char *hidpath, double Value, /* On success, return item count >0. When no notifications are available, * return 'error' or 'no event' code. */ -int HIDGetEvents(hid_dev_handle_t udev, HIDData_t **event, int eventsize) +int HIDGetEvents(TYPE_FD udev, HIDData_t **event, int eventsize) { unsigned char buf[SMALLBUF]; int itemCount = 0; diff --git a/drivers/libhid.h b/drivers/libhid.h index 0e513f9531..cb6a16c2cb 100644 --- a/drivers/libhid.h +++ b/drivers/libhid.h @@ -35,6 +35,9 @@ #include "hidtypes.h" #include "timehead.h" + +#include "win_shut_compat.h" + #ifdef SHUT_MODE #include "libshut.h" typedef SHUTDevice_t HIDDevice_t; @@ -94,17 +97,17 @@ extern reportbuf_t *reportbuf; /* buffer for most recent reports */ /* * HIDGetItemValue * -------------------------------------------------------------------------- */ -int HIDGetItemValue(hid_dev_handle_t udev, const char *hidpath, double *Value, usage_tables_t *utab); +int HIDGetItemValue(TYPE_FD udev, const char *hidpath, double *Value, usage_tables_t *utab); /* * HIDGetItemString * -------------------------------------------------------------------------- */ -char *HIDGetItemString(hid_dev_handle_t udev, const char *hidpath, char *buf, size_t buflen, usage_tables_t *utab); +char *HIDGetItemString(TYPE_FD udev, const char *hidpath, char *buf, size_t buflen, usage_tables_t *utab); /* * HIDSetItemValue * -------------------------------------------------------------------------- */ -bool_t HIDSetItemValue(hid_dev_handle_t udev, const char *hidpath, double value, usage_tables_t *utab); +bool_t HIDSetItemValue(TYPE_FD udev, const char *hidpath, double value, usage_tables_t *utab); /* * GetItemData @@ -119,27 +122,27 @@ char *HIDGetDataItem(const HIDData_t *hiddata, usage_tables_t *utab); /* * HIDGetDataValue * -------------------------------------------------------------------------- */ -int HIDGetDataValue(hid_dev_handle_t udev, HIDData_t *hiddata, double *Value, int age); +int HIDGetDataValue(TYPE_FD udev, HIDData_t *hiddata, double *Value, int age); /* * HIDSetDataValue * -------------------------------------------------------------------------- */ -int HIDSetDataValue(hid_dev_handle_t udev, HIDData_t *hiddata, double Value); +int HIDSetDataValue(TYPE_FD udev, HIDData_t *hiddata, double Value); /* * HIDGetIndexString * -------------------------------------------------------------------------- */ -char *HIDGetIndexString(hid_dev_handle_t udev, int Index, char *buf, size_t buflen); +char *HIDGetIndexString(TYPE_FD udev, int Index, char *buf, size_t buflen); /* * HIDGetEvents * -------------------------------------------------------------------------- */ -int HIDGetEvents(hid_dev_handle_t udev, HIDData_t **event, int eventlen); +int HIDGetEvents(TYPE_FD udev, HIDData_t **event, int eventlen); /* * Support functions * -------------------------------------------------------------------------- */ -void HIDDumpTree(hid_dev_handle_t udev, usage_tables_t *utab); +void HIDDumpTree(TYPE_FD udev, usage_tables_t *utab); const char *HIDDataType(const HIDData_t *hiddata); void free_report_buffer(reportbuf_t *rbuf); diff --git a/drivers/libshut.c b/drivers/libshut.c index 30651d0614..f72ac0b99e 100644 --- a/drivers/libshut.c +++ b/drivers/libshut.c @@ -180,16 +180,16 @@ struct my_hid_descriptor { /*! * SHUT functions for HID marshalling */ -int shut_get_descriptor(int upsfd, unsigned char type, +int shut_get_descriptor(TYPE_FD upsfd, unsigned char type, unsigned char index, void *buf, int size); -int shut_get_string_simple(int upsfd, int index, +int shut_get_string_simple(TYPE_FD upsfd, int index, char *buf, size_t buflen); -int libshut_get_report(int upsfd, int ReportId, +int libshut_get_report(TYPE_FD upsfd, int ReportId, unsigned char *raw_buf, int ReportSize ); -int shut_set_report(int upsfd, int id, unsigned char *pkt, int reportlen); -int libshut_get_interrupt(int upsfd, unsigned char *buf, +int shut_set_report(TYPE_FD upsfd, int id, unsigned char *pkt, int reportlen); +int libshut_get_interrupt(TYPE_FD upsfd, unsigned char *buf, int bufsize, int timeout); -void libshut_close(int upsfd); +void libshut_close(TYPE_FD upsfd); /* FIXME */ const char * shut_strerror(void) { return ""; } @@ -260,12 +260,12 @@ typedef union device_desc_data_t { } device_desc_data_t; #endif /* Low level SHUT (Serial HID UPS Transfer) routines */ -void setline(int upsfd, int set); -int shut_synchronise(int upsfd); -int shut_wait_ack(int upsfd); -int shut_interrupt_read(int upsfd, int ep, unsigned char *bytes, +void setline(TYPE_FD upsfd, int set); +int shut_synchronise(TYPE_FD upsfd); +int shut_wait_ack(TYPE_FD upsfd); +int shut_interrupt_read(TYPE_FD upsfd, int ep, unsigned char *bytes, int size, int timeout); -int shut_control_msg(int upsfd, int requesttype, int request, int value, +int shut_control_msg(TYPE_FD upsfd, int requesttype, int request, int value, int index, unsigned char *bytes, int size, int timeout); /* Data portability */ @@ -288,8 +288,8 @@ static void align_request(struct shut_ctrltransfer_s *ctrl ) * information. This callback should return a value > 0 if the device * is accepted, or < 1 if not. */ -int libshut_open(int *upsfd, SHUTDevice_t *curDevice, char *device_path, - int (*callback)(int upsfd, SHUTDevice_t *hd, unsigned char *rdbuf, int rdlen)) +int libshut_open(TYPE_FD *upsfd, SHUTDevice_t *curDevice, char *device_path, + int (*callback)(TYPE_FD upsfd, SHUTDevice_t *hd, unsigned char *rdbuf, int rdlen)) { int ret, res; unsigned char buf[20]; @@ -304,7 +304,7 @@ int libshut_open(int *upsfd, SHUTDevice_t *curDevice, char *device_path, upsdebugx(2, "libshut_open: using port %s", device_path); /* If device is still open, close it */ - if (*upsfd > 0) { + if (VALID_FD(*upsfd)) { ser_close(*upsfd, device_path); } @@ -471,9 +471,9 @@ int libshut_open(int *upsfd, SHUTDevice_t *curDevice, char *device_path, return -1; } -void libshut_close(int upsfd) +void libshut_close(TYPE_FD upsfd) { - if (upsfd < 1) { + if (!VALID_FD(upsfd)) { return; } @@ -483,10 +483,10 @@ void libshut_close(int upsfd) /* return the report of ID=type in report * return -1 on failure, report length on success */ -int libshut_get_report(int upsfd, int ReportId, +int libshut_get_report(TYPE_FD upsfd, int ReportId, unsigned char *raw_buf, int ReportSize ) { - if (upsfd < 1) { + if (!VALID_FD(upsfd)) { return 0; } @@ -501,12 +501,12 @@ int libshut_get_report(int upsfd, int ReportId, } /* return ReportSize upon success ; -1 otherwise */ -int libshut_set_report(int upsfd, int ReportId, +int libshut_set_report(TYPE_FD upsfd, int ReportId, unsigned char *raw_buf, int ReportSize ) { int ret; - if (upsfd < 1) { + if (!VALID_FD(upsfd)) { return 0; } @@ -525,11 +525,11 @@ int libshut_set_report(int upsfd, int ReportId, return ((ret == 0) ? ReportSize : ret); } -int libshut_get_string(int upsfd, int StringIdx, char *buf, size_t buflen) +int libshut_get_string(TYPE_FD upsfd, int StringIdx, char *buf, size_t buflen) { int ret; - if (upsfd < 1) { + if (!VALID_FD(upsfd)) { return -1; } @@ -542,12 +542,12 @@ int libshut_get_string(int upsfd, int StringIdx, char *buf, size_t buflen) return ret; } -int libshut_get_interrupt(int upsfd, unsigned char *buf, +int libshut_get_interrupt(TYPE_FD upsfd, unsigned char *buf, int bufsize, int timeout) { int ret; - if (upsfd < 1) { + if (!VALID_FD(upsfd)) { return -1; } @@ -582,9 +582,9 @@ shut_communication_subdriver_t shut_subdriver = { * set : 1 to set comm * set : 0 to stop commupsh. */ -void setline(int upsfd, int set) +void setline(TYPE_FD upsfd, int set) { - if (upsfd < 1) { + if (!VALID_FD(upsfd)) { return; } @@ -607,7 +607,7 @@ void setline(int upsfd, int set) * return TRUE on success, FALSE on failure * *****************************************************************************/ -int shut_synchronise(int upsfd) +int shut_synchronise(TYPE_FD upsfd) { int retCode = 0; u_char c = SHUT_SYNC_OFF, reply; @@ -669,7 +669,7 @@ u_char shut_checksum(const u_char *buf, int bufsize) } -int shut_packet_recv(int upsfd, u_char *Buf, int datalen) +int shut_packet_recv(TYPE_FD upsfd, u_char *Buf, int datalen) { u_char Start[2]; u_char Frame[8]; @@ -775,7 +775,7 @@ int shut_packet_recv(int upsfd, u_char *Buf, int datalen) } /**********************************************************************/ -int shut_interrupt_read(int upsfd, int ep, unsigned char *bytes, int size, +int shut_interrupt_read(TYPE_FD upsfd, int ep, unsigned char *bytes, int size, int timeout) { /* @@ -786,7 +786,7 @@ int shut_interrupt_read(int upsfd, int ep, unsigned char *bytes, int size, } /**********************************************************************/ -int shut_get_string_simple(int upsfd, int index, +int shut_get_string_simple(TYPE_FD upsfd, int index, char *buf, size_t buflen) { unsigned char tbuf[255]; /* Some devices choke on size > 255 */ @@ -834,7 +834,7 @@ int shut_get_string_simple(int upsfd, int index, * return 0 on success, -1 on failure, -2 on NACK * *********************************************************************/ -int shut_get_descriptor(int upsfd, unsigned char type, +int shut_get_descriptor(TYPE_FD upsfd, unsigned char type, unsigned char index, void *buf, int size) { memset(buf, 0, size); @@ -846,7 +846,7 @@ int shut_get_descriptor(int upsfd, unsigned char type, } /* Take care of a SHUT transfer (sending and receiving data) */ -int shut_control_msg(int upsfd, int requesttype, int request, +int shut_control_msg(TYPE_FD upsfd, int requesttype, int request, int value, int index, unsigned char *bytes, int size, int timeout) { unsigned char shut_pkt[11]; @@ -970,7 +970,7 @@ int shut_control_msg(int upsfd, int requesttype, int request, * returns 0 on success, -1 on error, -2 on NACK, -3 on NOTIFICATION * *********************************************************************/ -int shut_wait_ack(int upsfd) +int shut_wait_ack(TYPE_FD upsfd) { int retCode = -1; u_char c = '\0'; diff --git a/drivers/libshut.h b/drivers/libshut.h index 48f48bbca9..a5b78a2f2e 100644 --- a/drivers/libshut.h +++ b/drivers/libshut.h @@ -27,6 +27,8 @@ #ifndef LIBSHUT_H #define LIBSHUT_H +#include "win_shut_compat.h" + #include "main.h" /* for subdrv_info_t */ #include "nut_stdint.h" /* for uint16_t */ @@ -56,18 +58,18 @@ typedef struct SHUTDevice_s { typedef struct shut_communication_subdriver_s { const char *name; /* name of this subdriver */ const char *version; /* version of this subdriver */ - int (*open)(int *upsfd, /* try to open the next available */ + int (*open)(TYPE_FD *upsfd, /* try to open the next available */ SHUTDevice_t *curDevice, /* device matching USBDeviceMatcher_t */ char *device_path, - int (*callback)(int upsfd, SHUTDevice_t *hd, unsigned char *rdbuf, int rdlen)); - void (*close)(int upsfd); - int (*get_report)(int upsfd, int ReportId, + int (*callback)(TYPE_FD upsfd, SHUTDevice_t *hd, unsigned char *rdbuf, int rdlen)); + void (*close)(TYPE_FD upsfd); + int (*get_report)(TYPE_FD upsfd, int ReportId, unsigned char *raw_buf, int ReportSize ); - int (*set_report)(int upsfd, int ReportId, + int (*set_report)(TYPE_FD upsfd, int ReportId, unsigned char *raw_buf, int ReportSize ); - int (*get_string)(int upsfd, + int (*get_string)(TYPE_FD upsfd, int StringIdx, char *buf, size_t buflen); - int (*get_interrupt)(int upsfd, + int (*get_interrupt)(TYPE_FD upsfd, unsigned char *buf, int bufsize, int timeout); } shut_communication_subdriver_t; diff --git a/drivers/libusb.c b/drivers/libusb.c index 234b9f142a..e62ea4112b 100644 --- a/drivers/libusb.c +++ b/drivers/libusb.c @@ -32,6 +32,9 @@ #include "common.h" /* for xmalloc, upsdebugx prototypes */ #include "usb-common.h" #include "libusb.h" +#ifdef WIN32 +#include "wincompat.h" +#endif /* USB standard timeout */ #define USB_TIMEOUT 5000 @@ -218,6 +221,9 @@ static int libusb_open(usb_dev_handle **udevp, USBDevice_t *curDevice, USBDevice fatalx(EXIT_FAILURE, "Can't claim USB device [%04x:%04x]: %s", curDevice->VendorID, curDevice->ProductID, usb_strerror()); } #else +#ifdef WIN32 + usb_set_configuration(udev,1); +#endif if (usb_claim_interface(udev, 0) < 0) { fatalx(EXIT_FAILURE, "Can't claim USB device [%04x:%04x]: %s", curDevice->VendorID, curDevice->ProductID, usb_strerror()); } @@ -369,15 +375,18 @@ static int libusb_strerror(const int ret, const char *desc) case -ENOSYS: /* Function not implemented */ upslogx(LOG_DEBUG, "%s: %s", desc, usb_strerror()); return ret; - case -ETIMEDOUT: /* Connection timed out */ upsdebugx(2, "%s: Connection timed out", desc); return 0; +/* libusb win32 does not know EPROTO and EOVERFLOW, it only returns EIO for any + IO errors */ +#ifndef WIN32 case -EOVERFLOW: /* Value too large for defined data type */ case -EPROTO: /* Protocol error */ upsdebugx(2, "%s: %s", desc, usb_strerror()); return 0; +#endif default: /* Undetermined, log only */ upslogx(LOG_DEBUG, "%s: %s", desc, usb_strerror()); @@ -405,6 +414,10 @@ static int libusb_get_report(usb_dev_handle *udev, int ReportId, unsigned char * ReportId+(0x03<<8), /* HID_REPORT_TYPE_FEATURE */ 0, raw_buf, ReportSize, USB_TIMEOUT); +#ifdef WIN32 + errno = -ret; +#endif + /* Ignore "protocol stall" (for unsupported request) on control endpoint */ if (ret == -EPIPE) { return 0; @@ -427,6 +440,10 @@ static int libusb_set_report(usb_dev_handle *udev, int ReportId, unsigned char * ReportId+(0x03<<8), /* HID_REPORT_TYPE_FEATURE */ 0, raw_buf, ReportSize, USB_TIMEOUT); +#ifdef WIN32 + errno = -ret; +#endif + /* Ignore "protocol stall" (for unsupported request) on control endpoint */ if (ret == -EPIPE) { return 0; @@ -445,6 +462,10 @@ static int libusb_get_string(usb_dev_handle *udev, int StringIdx, char *buf, siz ret = usb_get_string_simple(udev, StringIdx, buf, buflen); +#ifdef WIN32 + errno = -ret; +#endif + return libusb_strerror(ret, __func__); } @@ -459,6 +480,9 @@ static int libusb_get_interrupt(usb_dev_handle *udev, unsigned char *buf, int bu /* FIXME: hardcoded interrupt EP => need to get EP descr for IF descr */ ret = usb_interrupt_read(udev, 0x81, (char *)buf, bufsize, timeout); +#ifdef WIN32 + errno = -ret; +#endif /* Clear stall condition */ if (ret == -EPIPE) { ret = usb_clear_halt(udev, 0x81); diff --git a/drivers/libusb.h b/drivers/libusb.h index 66d2633553..ab30ec7600 100644 --- a/drivers/libusb.h +++ b/drivers/libusb.h @@ -31,6 +31,8 @@ #ifndef LIBUSB_H #define LIBUSB_H +#include "win_shut_compat.h" + #include "main.h" /* for subdrv_info_t */ #include "usb-common.h" /* for USBDevice_t and USBDeviceMatcher_t */ diff --git a/drivers/main-hal.c b/drivers/main-hal.c index 8cd3275b50..5c844c99c0 100644 --- a/drivers/main-hal.c +++ b/drivers/main-hal.c @@ -38,7 +38,11 @@ extern char *udi; /* data which may be useful to the drivers */ +#ifndef WIN32 int upsfd = -1; +#else + HANDLE upsfd = INVALID_HANDLE_VALUE; +#endif char *device_path = NULL; const char *progname = NULL, *upsname = NULL, *device_name = NULL; diff --git a/drivers/main.c b/drivers/main.c index 7c2fc55f17..0f0c4c41dd 100644 --- a/drivers/main.c +++ b/drivers/main.c @@ -21,12 +21,21 @@ #include "dstate.h" /* data which may be useful to the drivers */ +#ifndef WIN32 int upsfd = -1; +#else + HANDLE upsfd = INVALID_HANDLE_VALUE; +#endif char *device_path = NULL; const char *progname = NULL, *upsname = NULL, *device_name = NULL; /* may be set by the driver to wake up while in dstate_poll_fds */ +#ifndef WIN32 int extrafd = -1; +#else + HANDLE extrafd = INVALID_HANDLE_VALUE; + static HANDLE mutex = INVALID_HANDLE_VALUE; +#endif /* for ser_open */ int do_lock_port = 1; @@ -441,13 +450,21 @@ static void exit_cleanup(void) dstate_free(); vartab_free(); + +#ifdef WIN32 + if(mutex != INVALID_HANDLE_VALUE) { + ReleaseMutex(mutex); + CloseHandle(mutex); + } +#endif } -static void set_exit_flag(int sig) +void set_exit_flag(int sig) { exit_flag = sig; } +#ifndef WIN32 static void setup_signals(void) { struct sigaction sa; @@ -464,6 +481,7 @@ static void setup_signals(void) sigaction(SIGHUP, &sa, NULL); sigaction(SIGPIPE, &sa, NULL); } +#endif int main(int argc, char **argv) { @@ -476,6 +494,24 @@ int main(int argc, char **argv) user = xstrdup(RUN_AS_USER); /* xstrdup: this gets freed at exit */ progname = xbasename(argv[0]); + +#ifdef WIN32 + const char * drv_name; + drv_name = xbasename(argv[0]); + /* remove trailing .exe */ + char * dot = strrchr(drv_name,'.'); + if( dot != NULL ) { + if(strcasecmp(dot, ".exe") == 0 ) { + progname = strdup(drv_name); + char * t = strrchr(progname,'.'); + *t = 0; + } + } + else { + progname = strdup(drv_name); + } +#endif + open_syslog(progname); upsdrv_banner(); @@ -566,6 +602,7 @@ int main(int argc, char **argv) /* Only switch to statepath if we're not powering off */ /* This avoid case where ie /var is umounted */ +#ifndef WIN32 if ((!do_forceshutdown) && (chdir(dflt_statepath()))) fatal_with_errno(EXIT_FAILURE, "Can't chdir to %s", dflt_statepath()); @@ -602,6 +639,42 @@ int main(int argc, char **argv) pidfn = xstrdup(buffer); writepid(pidfn); /* before backgrounding */ } +#else + char name[SMALLBUF]; + + snprintf(name,sizeof(name), "%s-%s",progname,upsname); + + mutex = CreateMutex(NULL,TRUE,name); + if(mutex == NULL ) { + if( GetLastError() != ERROR_ACCESS_DENIED ) { + fatalx(EXIT_FAILURE, "Can not create mutex %s : %d.\n",name,(int)GetLastError()); + } + } + + if (GetLastError() == ERROR_ALREADY_EXISTS || GetLastError() == ERROR_ACCESS_DENIED) { + upslogx(LOG_WARNING, "Duplicate driver instance detected! Terminating other driver!"); + for(i=0;i<10;i++) { + DWORD res; + sendsignal(name, COMMAND_STOP); + if(mutex != NULL ) { + res = WaitForSingleObject(mutex,1000); + if(res==WAIT_OBJECT_0) { + break; + } + } + else { + sleep(1); + mutex = CreateMutex(NULL,TRUE,name); + if(mutex != NULL ) { + break; + } + } + } + if(i >= 10 ) { + fatalx(EXIT_FAILURE, "Can not terminate the previous driver.\n"); + } + } +#endif /* clear out callback handler data */ memset(&upsh, '\0', sizeof(upsh)); diff --git a/drivers/main.h b/drivers/main.h index 3e3e84eb6e..d8407119a1 100644 --- a/drivers/main.h +++ b/drivers/main.h @@ -5,11 +5,19 @@ #include "upsconf.h" #include "dstate.h" #include "extstate.h" +#ifdef WIN32 +#include "wincompat.h" +#endif /* public functions & variables from main.c */ extern const char *progname, *upsname, *device_name; extern char *device_path; +#ifndef WIN32 extern int upsfd, extrafd, broken_driver, experimental_driver, do_lock_port, exit_flag; +#else +extern int broken_driver, experimental_driver, do_lock_port, exit_flag; +extern HANDLE upsfd, extrafd; +#endif extern unsigned int poll_interval; /* functions & variables required in each driver */ diff --git a/drivers/mge-hid.c b/drivers/mge-hid.c index 6383379dcd..86d4a5b7fc 100644 --- a/drivers/mge-hid.c +++ b/drivers/mge-hid.c @@ -912,7 +912,9 @@ static hid_info_t mge_hid2nut[] = /* Special case: boolean values that are mapped to ups.status and ups.alarm */ { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.ACPresent", NULL, NULL, HU_FLAG_QUICK_POLL, online_info }, { "BOOL", 0, 0, "UPS.PowerConverter.Input.[3].PresentStatus.Used", NULL, NULL, 0, mge_onbatt_info }, +#if 0 { "BOOL", 0, 0, "UPS.PowerConverter.Input.[1].PresentStatus.Used", NULL, NULL, 0, online_info }, +#endif { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Discharging", NULL, NULL, HU_FLAG_QUICK_POLL, discharging_info }, { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Charging", NULL, NULL, HU_FLAG_QUICK_POLL, charging_info }, /* FIXME: on Dell, the above requires an "AND" with "UPS.BatterySystem.Charger.Mode = 1" */ diff --git a/drivers/mge-shut.c b/drivers/mge-shut.c index 06398edf0e..66df796dc6 100644 --- a/drivers/mge-shut.c +++ b/drivers/mge-shut.c @@ -547,35 +547,46 @@ int shut_wait_ack (void) static int char_read (char *bytes, int size, int read_timeout) { struct timeval serial_timeout; + int bytes_read = 0; + int now; + + serial_timeout.tv_usec = (read_timeout % 1000) * 1000; + serial_timeout.tv_sec = (read_timeout / 1000); + +#ifndef WIN32 fd_set readfs; - int readen = 0; int rc = 0; - FD_ZERO (&readfs); FD_SET (upsfd, &readfs); - serial_timeout.tv_usec = (read_timeout % 1000) * 1000; - serial_timeout.tv_sec = (read_timeout / 1000); - rc = select (upsfd + 1, &readfs, NULL, NULL, &serial_timeout); if (0 == rc) return -2; /* timeout */ if (FD_ISSET (upsfd, &readfs)) { - int now = read (upsfd, bytes, size - readen); + now = read (upsfd, bytes, size - bytes_read); if (now < 0) { return -1; } else { bytes += now; - readen += now; + bytes_read += now; } } else { return -1; } - return readen; +#else + now = select_read(upsfd,bytes,size - bytes_read, serial_timeout.tv_sec, serial_timeout.tv_usec); + if( now == -1 ) { + return -1; + } + bytes += now; + bytes_read += now; + +#endif + return bytes_read; } /********************************************************************** @@ -1137,9 +1148,9 @@ int hid_init_device() } /* translate HID string path to numeric path and return path depth */ -ushort lookup_path(const char *HIDpath, HIDData_t *data) +unsigned short lookup_path(const char *HIDpath, HIDData_t *data) { - ushort i = 0, cond = 1; + unsigned short i = 0, cond = 1; int cur_usage; char buf[MAX_STRING]; char *start, *end; diff --git a/drivers/mge-utalk.c b/drivers/mge-utalk.c index 2935e91d68..c3ef9dc03c 100644 --- a/drivers/mge-utalk.c +++ b/drivers/mge-utalk.c @@ -53,7 +53,9 @@ */ #include +#ifndef WIN32 #include +#endif #include "timehead.h" #include "main.h" #include "serial.h" @@ -162,17 +164,28 @@ void upsdrv_makevartable(void) void upsdrv_initups(void) { char buf[BUFFLEN]; +#ifndef WIN32 int RTS = TIOCM_RTS; +#endif upsfd = ser_open(device_path); ser_set_speed(upsfd, device_path, B2400); /* read command line/conf variable that affect comm. */ +#ifndef WIN32 if (testvar ("oldmac")) RTS = ~TIOCM_RTS; /* Init serial line */ ioctl(upsfd, TIOCMBIC, &RTS); +#else + if (testvar ("oldmac")) { + EscapeCommFunction(((serial_handler_t *)upsfd)->handle,CLRRTS); + } + else { + EscapeCommFunction(((serial_handler_t *)upsfd)->handle,SETRTS); + } +#endif enable_ups_comm(); /* Try to set "Low Battery Level" (if supported and given) */ @@ -890,7 +903,7 @@ static int mge_command(char *reply, int replylen, const char *fmt, ...) if (write(upsfd, p, 1) != 1) return -1; - + bytes_sent++; usleep(MGE_CHAR_DELAY); } diff --git a/drivers/mge-xml.c b/drivers/mge-xml.c index adb8262323..e0a514a4ef 100644 --- a/drivers/mge-xml.c +++ b/drivers/mge-xml.c @@ -31,6 +31,9 @@ #include "netxml-ups.h" #include "mge-xml.h" +#ifdef WIN32 +#include "wincompat.h" +#endif #define MGE_XML_VERSION "MGEXML/0.22" #define MGE_XML_INITUPS "/" #define MGE_XML_INITINFO "/mgeups/product.xml /product.xml /ws/product.xml" diff --git a/drivers/microdowell.c b/drivers/microdowell.c index a1d6605312..28b901d182 100644 --- a/drivers/microdowell.c +++ b/drivers/microdowell.c @@ -32,7 +32,9 @@ #include "main.h" #include "serial.h" +#ifndef WIN32 #include +#endif #include "timehead.h" diff --git a/drivers/netxml-ups.c b/drivers/netxml-ups.c index 2568ff06f8..c9b8566dec 100644 --- a/drivers/netxml-ups.c +++ b/drivers/netxml-ups.c @@ -45,6 +45,11 @@ /** *_OBJECT query multi-part body boundary */ #define FORM_POST_BOUNDARY "NUT-NETXML-UPS-OBJECTS" +#ifdef WIN32 /* FIXME ?? skip alarm handling */ +#define HAVE_NE_SET_CONNECT_TIMEOUT 1 +#define HAVE_NE_SOCK_CONNECT_TIMEOUT 1 +#endif + /* driver description structure */ upsdrv_info_t upsdrv_info = { DRIVER_NAME, @@ -273,7 +278,10 @@ void upsdrv_initinfo(void) dstate_setinfo("driver.version.data", "%s", subdriver->version); if (testvar("subscribe") && (netxml_alarm_subscribe(subdriver->subscribe) == NE_OK)) { +/* TODO: port extrafd to Windows */ +#ifndef WIN32 extrafd = ne_sock_fd(sock); +#endif time(&lastheard); } @@ -328,13 +336,19 @@ void upsdrv_updateinfo(void) ne_sock_close(sock); if (netxml_alarm_subscribe(subdriver->subscribe) == NE_OK) { +/* TODO: port extrafd to Windows */ +#ifndef WIN32 extrafd = ne_sock_fd(sock); +#endif time(&lastheard); return; } dstate_datastale(); +/* TODO: port extrafd to Windows */ +#ifndef WIN32 extrafd = -1; +#endif return; } } @@ -606,7 +620,11 @@ void upsdrv_initups(void) /* if debug level is set, direct output to stderr */ if (!nut_debug_level) { +#ifndef WIN32 fp = fopen("/dev/null", "w"); +#else + fp = fopen("nul", "w"); +#endif } else { fp = stderr; } diff --git a/drivers/powercom.h b/drivers/powercom.h index 8d0dab6a3f..52dd7dc248 100644 --- a/drivers/powercom.h +++ b/drivers/powercom.h @@ -24,7 +24,9 @@ /* C-libary includes */ #include #include +#ifndef WIN32 #include +#endif #include #include "serial.h" #include diff --git a/drivers/richcomm_usb.c b/drivers/richcomm_usb.c index 0b9b8f2742..01eafc6893 100644 --- a/drivers/richcomm_usb.c +++ b/drivers/richcomm_usb.c @@ -181,6 +181,9 @@ static int driver_callback(usb_dev_handle *handle, USBDevice_t *device) return -1; } +#ifdef WIN + usb_set_configuration(handle, 0); +#endif if (usb_claim_interface(handle, 0) < 0) { upsdebugx(5, "Can't claim USB interface"); return -1; @@ -358,7 +361,13 @@ void upsdrv_initups(void) for (i = 0; usb_device_open(&udev, &usbdevice, &device_matcher, &driver_callback) < 0; i++) { +#ifndef WIN32 if ((i < 32) && (sleep(5) == 0)) { +#else +/*FIXME*/ + sleep(5); + if ((i < 32)) { +#endif usb_comm_fail("Can't open USB device, retrying ..."); continue; } diff --git a/drivers/riello_usb.c b/drivers/riello_usb.c index 799fb4437c..0d61b00823 100644 --- a/drivers/riello_usb.c +++ b/drivers/riello_usb.c @@ -354,8 +354,12 @@ int riello_command(uint8_t *cmd, uint8_t *buf, uint16_t length, uint16_t buflen) break; case -ETIMEDOUT: /* Connection timed out */ - case -EOVERFLOW: /* Value too large for defined data type */ - case -EPROTO: /* Protocol error */ +#ifndef WIN32 +/* libusb win32 does not know EPROTO and EOVERFLOW, it only returns EIO for any + IO errors */ + case -EOVERFLOW: /* Value too large for defined data type */ + case -EPROTO: /* Protocol error */ +#endif default: break; } diff --git a/drivers/serial.c b/drivers/serial.c index 9c6129d334..9cf660b5ee 100644 --- a/drivers/serial.c +++ b/drivers/serial.c @@ -22,12 +22,14 @@ #include "serial.h" #include "main.h" +#ifndef WIN32 #include #include +#include +#endif #include #include #include -#include #include #ifdef HAVE_UU_LOCK @@ -39,8 +41,10 @@ static void ser_open_error(const char *port) { struct stat fs; +#ifndef WIN32 struct passwd *user; struct group *group; +#endif printf("\n"); @@ -54,6 +58,8 @@ static void ser_open_error(const char *port) fatalx(EXIT_FAILURE, "Fatal error: unusable configuration"); } +/* TODO */ +#ifndef WIN32 user = getpwuid(getuid()); if (user) @@ -72,6 +78,7 @@ static void ser_open_error(const char *port) printf("Serial port group: %s (%d)\n", group->gr_name, (int) fs.st_gid); +#endif printf(" Mode of port: %04o\n\n", (int) fs.st_mode & 07777); printf("Things to try:\n\n"); @@ -83,12 +90,12 @@ static void ser_open_error(const char *port) fatalx(EXIT_FAILURE, "Fatal error: unusable configuration"); } -static void lock_set(int fd, const char *port) +static void lock_set(TYPE_FD fd, const char *port) { int ret; - if (fd < 0) - fatal_with_errno(EXIT_FAILURE, "lock_set: programming error: fd = %d", fd); + if (fd == ERROR_FD) + fatal_with_errno(EXIT_FAILURE, "lock_set: programming error: fd = %d", PRINT_FD(fd)); if (do_lock_port == 0) return; @@ -118,41 +125,41 @@ static void lock_set(int fd, const char *port) #else + ret = 0; /* Make compiler happy */ + ret = ret; upslog_with_errno(LOG_WARNING, "Warning: no locking method is available"); #endif } /* Non fatal version of ser_open */ -int ser_open_nf(const char *port) - +TYPE_FD ser_open_nf(const char *port) { - int fd; + TYPE_FD fd; fd = open(port, O_RDWR | O_NOCTTY | O_EXCL | O_NONBLOCK); - if (fd < 0) { - return -1; - } + if (fd == ERROR_FD) + return ERROR_FD; lock_set(fd, port); return fd; } -int ser_open(const char *port) +TYPE_FD ser_open(const char *port) { - int res; + TYPE_FD res; res = ser_open_nf(port); - if(res == -1) { + if(res == ERROR_FD) { ser_open_error(port); } return res; } -int ser_set_speed_nf(int fd, const char *port, speed_t speed) +int ser_set_speed_nf(TYPE_FD fd, const char *port, speed_t speed) { struct termios tio; @@ -179,8 +186,7 @@ int ser_set_speed_nf(int fd, const char *port, speed_t speed) return 0; } - -int ser_set_speed(int fd, const char *port, speed_t speed) +int ser_set_speed(TYPE_FD fd, const char *port, speed_t speed) { int res; @@ -192,7 +198,8 @@ int ser_set_speed(int fd, const char *port, speed_t speed) return 0; } -static int ser_set_control(int fd, int line, int state) +#ifndef WIN32 +static int ser_set_control(TYPE_FD fd, int line, int state) { if (state) { return ioctl(fd, TIOCMBIS, &line); @@ -201,7 +208,7 @@ static int ser_set_control(int fd, int line, int state) } } -int ser_set_dtr(int fd, int state) +int ser_set_dtr(TYPE_FD fd, int state) { return ser_set_control(fd, TIOCM_DTR, state); } @@ -210,8 +217,45 @@ int ser_set_rts(int fd, int state) { return ser_set_control(fd, TIOCM_RTS, state); } +#else +int ser_set_dtr(TYPE_FD fd, int state) +{ + DWORD action; + + if(state == 0) { + action = CLRDTR; + } + else { + action = SETDTR; + } -static int ser_get_control(int fd, int line) + /* Success */ + if( EscapeCommFunction(fd->handle,action) != 0) { + return 0; + } + return -1; +} + +int ser_set_rts(TYPE_FD fd, int state) +{ + DWORD action; + + if(state == 0) { + action = CLRRTS; + } + else { + action = SETRTS; + } + /* Success */ + if( EscapeCommFunction(fd->handle,action) != 0) { + return 0; + } + return -1; +} +#endif + +#ifndef WIN32 +static int ser_get_control(TYPE_FD fd, int line) { int flags; @@ -220,30 +264,56 @@ static int ser_get_control(int fd, int line) return (flags & line); } -int ser_get_dsr(int fd) +int ser_get_dsr(TYPE_FD fd) { return ser_get_control(fd, TIOCM_DSR); } -int ser_get_cts(int fd) +int ser_get_cts(TYPE_FD fd) { return ser_get_control(fd, TIOCM_CTS); } -int ser_get_dcd(int fd) +int ser_get_dcd(TYPE_FD fd) { return ser_get_control(fd, TIOCM_CD); } +#else +int ser_get_dsr(TYPE_FD fd) +{ + int flags; + + w32_getcomm(fd->handle, &flags); + return (flags & TIOCM_DSR); +} + +int ser_get_cts(TYPE_FD fd) +{ + int flags; + + w32_getcomm(fd->handle, &flags); + return (flags & TIOCM_CTS); +} + +int ser_get_dcd(TYPE_FD fd) +{ + int flags; + + w32_getcomm(fd->handle, &flags); + return (flags & TIOCM_CD); +} + +#endif -int ser_flush_io(int fd) +int ser_flush_io(TYPE_FD fd) { return tcflush(fd, TCIOFLUSH); } -int ser_close(int fd, const char *port) +int ser_close(TYPE_FD fd, const char *port) { - if (fd < 0) - fatal_with_errno(EXIT_FAILURE, "ser_close: programming error: fd=%d port=%s", fd, port); + if (fd == ERROR_FD) + fatal_with_errno(EXIT_FAILURE, "ser_close: programming error: fd=%d port=%s", PRINT_FD(fd), port); if (close(fd) != 0) return -1; @@ -256,12 +326,12 @@ int ser_close(int fd, const char *port) return 0; } -int ser_send_char(int fd, unsigned char ch) +int ser_send_char(TYPE_FD fd, unsigned char ch) { return ser_send_buf_pace(fd, 0, &ch, 1); } -static int send_formatted(int fd, const char *fmt, va_list va, unsigned long d_usec) +static int send_formatted(TYPE_FD fd, const char *fmt, va_list va, unsigned long d_usec) { int ret; char buf[LARGEBUF]; @@ -276,7 +346,7 @@ static int send_formatted(int fd, const char *fmt, va_list va, unsigned long d_u } /* send the results of the format string with d_usec delay after each char */ -int ser_send_pace(int fd, unsigned long d_usec, const char *fmt, ...) +int ser_send_pace(TYPE_FD fd, unsigned long d_usec, const char *fmt, ...) { int ret; va_list ap; @@ -291,7 +361,7 @@ int ser_send_pace(int fd, unsigned long d_usec, const char *fmt, ...) } /* send the results of the format string with no delay */ -int ser_send(int fd, const char *fmt, ...) +int ser_send(TYPE_FD fd, const char *fmt, ...) { int ret; va_list ap; @@ -306,13 +376,13 @@ int ser_send(int fd, const char *fmt, ...) } /* send buflen bytes from buf with no delay */ -int ser_send_buf(int fd, const void *buf, size_t buflen) +int ser_send_buf(TYPE_FD fd, const void *buf, size_t buflen) { return ser_send_buf_pace(fd, 0, buf, buflen); } /* send buflen bytes from buf with d_usec delay after each char */ -int ser_send_buf_pace(int fd, unsigned long d_usec, const void *buf, +int ser_send_buf_pace(TYPE_FD fd, unsigned long d_usec, const void *buf, size_t buflen) { int ret; @@ -333,12 +403,12 @@ int ser_send_buf_pace(int fd, unsigned long d_usec, const void *buf, return sent; } -int ser_get_char(int fd, void *ch, long d_sec, long d_usec) +int ser_get_char(TYPE_FD fd, void *ch, long d_sec, long d_usec) { return select_read(fd, ch, 1, d_sec, d_usec); } -int ser_get_buf(int fd, void *buf, size_t buflen, long d_sec, long d_usec) +int ser_get_buf(TYPE_FD fd, void *buf, size_t buflen, long d_sec, long d_usec) { memset(buf, '\0', buflen); @@ -346,7 +416,7 @@ int ser_get_buf(int fd, void *buf, size_t buflen, long d_sec, long d_usec) } /* keep reading until buflen bytes are received or a timeout occurs */ -int ser_get_buf_len(int fd, void *buf, size_t buflen, long d_sec, long d_usec) +int ser_get_buf_len(TYPE_FD fd, void *buf, size_t buflen, long d_sec, long d_usec) { int ret; size_t recv; @@ -368,7 +438,7 @@ int ser_get_buf_len(int fd, void *buf, size_t buflen, long d_sec, long d_usec) /* reads a line up to , discarding anything else that may follow, with callouts to the handler if anything matches the alertset */ -int ser_get_line_alert(int fd, void *buf, size_t buflen, char endchar, +int ser_get_line_alert(TYPE_FD fd, void *buf, size_t buflen, char endchar, const char *ignset, const char *alertset, void handler(char ch), long d_sec, long d_usec) { @@ -412,14 +482,14 @@ int ser_get_line_alert(int fd, void *buf, size_t buflen, char endchar, } /* as above, only with no alertset handling (just a wrapper) */ -int ser_get_line(int fd, void *buf, size_t buflen, char endchar, +int ser_get_line(TYPE_FD fd, void *buf, size_t buflen, char endchar, const char *ignset, long d_sec, long d_usec) { return ser_get_line_alert(fd, buf, buflen, endchar, ignset, "", NULL, d_sec, d_usec); } -int ser_flush_in(int fd, const char *ignset, int verbose) +int ser_flush_in(TYPE_FD fd, const char *ignset, int verbose) { int ret, extra = 0; char ch; diff --git a/drivers/serial.h b/drivers/serial.h index 5fb3a45129..09be4319f3 100644 --- a/drivers/serial.h +++ b/drivers/serial.h @@ -5,75 +5,102 @@ #include "config.h" +#ifndef WIN32 #if defined(HAVE_SYS_TERMIOS_H) # include /* for speed_t */ #else # include #endif /* HAVE_SYS_TERMIOS_H */ +#else /* WIN32 */ +#include "common.h" +#include "wincompat.h" +#endif /* WIN32 */ /* limit the amount of spew that goes in the syslog when we lose the UPS */ #define SER_ERR_LIMIT 10 /* start limiting after 10 in a row */ #define SER_ERR_RATE 100 /* then only print every 100th error */ -int ser_open_nf(const char *port); -int ser_open(const char *port); +/* porting stuff for WIN32 */ +#ifndef WIN32 +#define ERROR_FD (-1) +#define TYPE_FD int +#define PRINT_FD(x) x +#else +#define ERROR_FD (NULL) +#define TYPE_FD serial_handler_t * +#define PRINT_FD(x) (int)x->handle + +/* TODO : support "open" flags */ +#define O_NONBLOCK 0 +#define O_NOCTTY 0 +#endif -int ser_set_speed_nf(int fd, const char *port, speed_t speed); -int ser_set_speed(int fd, const char *port, speed_t speed); +TYPE_FD ser_open_nf(const char *port); +TYPE_FD ser_open(const char *port); + +int ser_set_speed(TYPE_FD fd, const char *port, speed_t speed); +int ser_set_speed_nf(TYPE_FD fd, const char *port, speed_t speed); /* set the state of modem control lines */ -int ser_set_dtr(int fd, int state); -int ser_set_rts(int fd, int state); +int ser_set_dtr(TYPE_FD fd, int state); +int ser_set_rts(TYPE_FD fd, int state); /* get the status of modem control lines */ -int ser_get_dsr(int fd); -int ser_get_cts(int fd); -int ser_get_dcd(int fd); +int ser_get_dsr(TYPE_FD fd); +int ser_get_cts(TYPE_FD fd); +int ser_get_dcd(TYPE_FD fd); -int ser_flush_io(int fd); +int ser_flush_io(TYPE_FD fd); -int ser_close(int fd, const char *port); +int ser_close(TYPE_FD fd, const char *port); -int ser_send_char(int fd, unsigned char ch); +int ser_send_char(TYPE_FD fd, unsigned char ch); /* send the results of the format string with d_usec delay after each char */ -int ser_send_pace(int fd, unsigned long d_usec, const char *fmt, ...) +int ser_send_pace(TYPE_FD fd, unsigned long d_usec, const char *fmt, ...) __attribute__ ((__format__ (__printf__, 3, 4))); /* send the results of the format string with no delay */ -int ser_send(int fd, const char *fmt, ...) +int ser_send(TYPE_FD fd, const char *fmt, ...) __attribute__ ((__format__ (__printf__, 2, 3))); /* send buflen bytes from buf with no delay */ -int ser_send_buf(int fd, const void *buf, size_t buflen); +int ser_send_buf(TYPE_FD fd, const void *buf, size_t buflen); /* send buflen bytes from buf with d_usec delay after each char */ -int ser_send_buf_pace(int fd, unsigned long d_usec, const void *buf, +int ser_send_buf_pace(TYPE_FD fd, unsigned long d_usec, const void *buf, size_t buflen); -int ser_get_char(int fd, void *ch, long d_sec, long d_usec); +int ser_get_char(TYPE_FD fd, void *ch, long d_sec, long d_usec); -int ser_get_buf(int fd, void *buf, size_t buflen, long d_sec, long d_usec); +int ser_get_buf(TYPE_FD fd, void *buf, size_t buflen, long d_sec, long d_usec); /* keep reading until buflen bytes are received or a timeout occurs */ -int ser_get_buf_len(int fd, void *buf, size_t buflen, long d_sec, long d_usec); +int ser_get_buf_len(TYPE_FD fd, void *buf, size_t buflen, long d_sec, long d_usec); /* reads a line up to , discarding anything else that may follow, with callouts to the handler if anything matches the alertset */ -int ser_get_line_alert(int fd, void *buf, size_t buflen, char endchar, +int ser_get_line_alert(TYPE_FD fd, void *buf, size_t buflen, char endchar, const char *ignset, const char *alertset, void handler (char ch), long d_sec, long d_usec); /* as above, only with no alertset handling (just a wrapper) */ -int ser_get_line(int fd, void *buf, size_t buflen, char endchar, +int ser_get_line(TYPE_FD fd, void *buf, size_t buflen, char endchar, const char *ignset, long d_sec, long d_usec); -int ser_flush_in(int fd, const char *ignset, int verbose); +int ser_flush_in(TYPE_FD fd, const char *ignset, int verbose); /* unified failure reporting: call these often */ void ser_comm_fail(const char *fmt, ...) __attribute__ ((__format__ (__printf__, 1, 2))); void ser_comm_good(void); +#ifdef WIN32 +#define open(a,b) w32_serial_open(a,b) +#define close(a) w32_serial_close(a) +#define read(a,b,c) w32_serial_read(a,b,c,INFINITE) +#define write(a,b,c) w32_serial_write(a,b,c) +#endif + #endif /* SERIAL_H_SEEN */ diff --git a/drivers/snmp-ups.h b/drivers/snmp-ups.h index 8b281df131..78e1e86aee 100644 --- a/drivers/snmp-ups.h +++ b/drivers/snmp-ups.h @@ -72,9 +72,14 @@ for each OID request we made), instead of sending many small packets #undef HAVE_DMALLOC_H #endif +#ifdef WIN32 +#undef random +#undef _WIN32_WINNT +#endif #include #include +#undef DISABLE_MIB_LOADING /* Force numeric OIDs by disabling MIB loading */ #define DISABLE_MIB_LOADING 1 diff --git a/drivers/upsdrvctl.c b/drivers/upsdrvctl.c index 623f753840..e33baaf96f 100644 --- a/drivers/upsdrvctl.c +++ b/drivers/upsdrvctl.c @@ -22,7 +22,11 @@ #include #include #include +#ifndef WIN32 #include +#else +#include "wincompat.h" +#endif #include "config.h" #include "proto.h" @@ -123,10 +127,11 @@ static void stop_driver(const ups_t *ups) { char pidfn[SMALLBUF]; int ret; - struct stat fs; upsdebugx(1, "Stopping UPS: %s", ups->upsname); +#ifndef WIN32 + struct stat fs; snprintf(pidfn, sizeof(pidfn), "%s/%s-%s.pid", altpidpath(), ups->driver, ups->upsname); ret = stat(pidfn, &fs); @@ -143,12 +148,20 @@ static void stop_driver(const ups_t *ups) return; } +#else + snprintf(pidfn, sizeof(pidfn), "%s-%s",ups->driver, ups->upsname); +#endif + upsdebugx(2, "Sending signal to %s", pidfn); if (testmode) return; +#ifndef WIN32 ret = sendsignalfn(pidfn, SIGTERM); +#else + ret = sendsignal(pidfn, COMMAND_STOP); +#endif if (ret < 0) { upslog_with_errno(LOG_ERR, "Stopping %s failed", pidfn); @@ -157,11 +170,13 @@ static void stop_driver(const ups_t *ups) } } +#ifndef WIN32 static void waitpid_timeout(const int sig) { /* do nothing */ return; } +#endif /* print out a command line at the given debug level. */ static void debugcmdline(int level, const char *msg, char *const argv[]) @@ -179,6 +194,7 @@ static void debugcmdline(int level, const char *msg, char *const argv[]) static void forkexec(char *const argv[], const ups_t *ups) { +#ifndef WIN32 int ret; pid_t pid; @@ -241,6 +257,57 @@ static void forkexec(char *const argv[], const ups_t *ups) /* shouldn't get here */ fatal_with_errno(EXIT_FAILURE, "execv"); +#else + BOOL ret; + DWORD res; + DWORD exit_code = 0; + char commandline[SMALLBUF]; + STARTUPINFO StartupInfo; + PROCESS_INFORMATION ProcessInformation; + int i = 1; + + memset(&StartupInfo,0,sizeof(STARTUPINFO)); + + /* the command line is made of the driver name followed by args */ + snprintf(commandline,sizeof(commandline),"%s", ups->driver); + while( argv[i] != NULL ) { + snprintfcat(commandline, sizeof(commandline), " %s", argv[i]); + i++; + } + + ret = CreateProcess( + argv[0], + commandline, + NULL, + NULL, + FALSE, + CREATE_NEW_PROCESS_GROUP, + NULL, + NULL, + &StartupInfo, + &ProcessInformation + ); + + if( ret == 0 ) { + fatal_with_errno(EXIT_FAILURE, "execv"); + } + + /* Wait a bit then look at driver process. + Unlike under Linux, Windows spwan drivers directly. If the driver is alive, all is OK. + An optimization can probably be implemented to prevent waiting so much time when all is OK. + */ + res = WaitForSingleObject(ProcessInformation.hProcess, + (ups->maxstartdelay!=-1?ups->maxstartdelay:maxstartdelay)*1000); + + if (res != WAIT_TIMEOUT) { + GetExitCodeProcess( ProcessInformation.hProcess, &exit_code ); + upslogx(LOG_WARNING, "Driver failed to start (exit status=%d)", ret); + exec_error++; + return; + } + + return; +#endif } static void start_driver(const ups_t *ups) @@ -252,7 +319,11 @@ static void start_driver(const ups_t *ups) upsdebugx(1, "Starting UPS: %s", ups->upsname); +#ifndef WIN32 snprintf(dfn, sizeof(dfn), "%s/%s", driverpath, ups->driver); +#else + snprintf(dfn, sizeof(dfn), "%s/%s.exe", driverpath, ups->driver); +#endif ret = stat(dfn, &fs); if (ret < 0) @@ -311,7 +382,11 @@ static void shutdown_driver(const ups_t *ups) upsdebugx(1, "Shutdown UPS: %s", ups->upsname); +#ifndef WIN32 snprintf(dfn, sizeof(dfn), "%s/%s", driverpath, ups->driver); +#else + snprintf(dfn, sizeof(dfn), "%s/%s.exe", driverpath, ups->driver); +#endif argv[arg++] = dfn; argv[arg++] = (char *)"-a"; /* FIXME: cast away const */ @@ -482,7 +557,11 @@ int main(int argc, char **argv) if (!command) fatalx(EXIT_FAILURE, "Error: unrecognized command [%s]", argv[0]); +#ifndef WIN32 driverpath = xstrdup(DRVPATH); /* set default */ +#else + driverpath = getfullpath(NULL); /* Relative path in WIN32 */ +#endif atexit(exit_cleanup); diff --git a/drivers/usbhid-ups.c b/drivers/usbhid-ups.c index fcf628d11a..4e838080b0 100644 --- a/drivers/usbhid-ups.c +++ b/drivers/usbhid-ups.c @@ -29,11 +29,15 @@ #define DRIVER_NAME "Generic HID driver" #define DRIVER_VERSION "0.38" +#include "win_shut_compat.h" #include "main.h" #include "libhid.h" #include "usbhid-ups.h" #include "hidparser.h" #include "hidtypes.h" +#ifdef WIN32 +#include "wincompat.h" +#endif /* include all known subdrivers */ #include "mge-hid.h" @@ -112,7 +116,7 @@ bool_t use_interrupt_pipe = TRUE; bool_t use_interrupt_pipe = FALSE; #endif static time_t lastpoll; /* Timestamp the last polling */ -hid_dev_handle_t udev; +TYPE_FD udev; /* support functions */ static hid_info_t *find_nut_info(const char *varname); @@ -125,7 +129,7 @@ static void ups_status_set(void); static bool_t hid_ups_walk(walkmode_t mode); static int reconnect_ups(void); static int ups_infoval_set(hid_info_t *item, double value); -static int callback(hid_dev_handle_t udev, HIDDevice_t *hd, unsigned char *rdbuf, int rdlen); +static int callback(TYPE_FD udev, HIDDevice_t *hd, unsigned char *rdbuf, int rdlen); #ifdef DEBUG static double interval(void); #endif @@ -152,7 +156,7 @@ typedef enum { BYPASSAUTO, /* on automatic bypass */ BYPASSMAN, /* on manual/service bypass */ OFF, /* ups is off */ - CAL, /* calibration */ + CALIB, /* calibration */ OVERHEAT, /* overheat; Belkin, TrippLite */ COMMFAULT, /* UPS fault; Belkin, TrippLite */ DEPLETED, /* battery depleted; Belkin */ @@ -195,7 +199,7 @@ static status_lkp_t status_info[] = { { "bypassauto", STATUS(BYPASSAUTO) }, { "bypassman", STATUS(BYPASSMAN) }, { "off", STATUS(OFF) }, - { "cal", STATUS(CAL) }, + { "cal", STATUS(CALIB) }, { "overheat", STATUS(OVERHEAT) }, { "commfault", STATUS(COMMFAULT) }, { "depleted", STATUS(DEPLETED) }, @@ -1057,7 +1061,7 @@ static void process_boolean_info(const char *nutvalue) upsdebugx(5, "Warning: %s not in list of known values", nutvalue); } -static int callback(hid_dev_handle_t udev, HIDDevice_t *hd, unsigned char *rdbuf, int rdlen) +static int callback(TYPE_FD udev, HIDDevice_t *hd, unsigned char *rdbuf, int rdlen) { int i; const char *mfr = NULL, *model = NULL, *serial = NULL; @@ -1268,8 +1272,12 @@ static bool_t hid_ups_walk(walkmode_t mode) continue; case -ETIMEDOUT: /* Connection timed out */ +/* libusb win32 does not know EPROTO and EOVERFLOW, it only returns EIO for any + IO errors */ +#ifndef WIN32 case -EOVERFLOW: /* Value too large for defined data type */ case -EPROTO: /* Protocol error */ +#endif case -EPIPE: /* Broken pipe */ default: /* Don't know what happened, try again later... */ @@ -1424,7 +1432,7 @@ static void ups_status_set(void) if (ups_status & STATUS(OFF)) { status_set("OFF"); /* ups is off */ } - if (ups_status & STATUS(CAL)) { + if (ups_status & STATUS(CALIB)) { status_set("CAL"); /* calibration */ } } diff --git a/drivers/usbhid-ups.h b/drivers/usbhid-ups.h index 4f4b3c320e..145c663446 100644 --- a/drivers/usbhid-ups.h +++ b/drivers/usbhid-ups.h @@ -32,8 +32,9 @@ #include #include "config.h" #include "libhid.h" +#include "libshut.h" -extern hid_dev_handle_t udev; +extern TYPE_FD udev; extern bool_t use_interrupt_pipe; /* Set to FALSE if interrupt reports should not be used */ /* Driver's parameters */ diff --git a/drivers/voltronic_usb.c b/drivers/voltronic_usb.c index 4fa350d10b..633f7396a3 100644 --- a/drivers/voltronic_usb.c +++ b/drivers/voltronic_usb.c @@ -195,8 +195,12 @@ int voltronic_command(const char *cmd, char *buf, size_t buflen) break; case -ETIMEDOUT: /* Connection timed out */ - case -EOVERFLOW: /* Value too large for defined data type */ - case -EPROTO: /* Protocol error */ +#ifndef WIN32 +/* libusb win32 does not know EPROTO and EOVERFLOW, it only returns EIO for any + IO errors */ + case -EOVERFLOW: /* Value too large for defined data type */ + case -EPROTO: /* Protocol error */ +#endif default: break; } diff --git a/drivers/win_shut_compat.h b/drivers/win_shut_compat.h new file mode 100644 index 0000000000..7d76e9a945 --- /dev/null +++ b/drivers/win_shut_compat.h @@ -0,0 +1,45 @@ +/*! + * @file win_shut_comapt.h + * @brief Windows specific macros for shut protocol + * + * @author Copyright (C) 2013 + * Arnaud Quette + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * -------------------------------------------------------------------------- */ + +#ifndef WIN_SHUT_COMPAT +#define WIN_SHUT_COMPAT + +#ifndef WIN32 +#define ERROR_FD (-1) +#define VALID_FD(a) (a>0) +#ifdef SHUT_MODE +#define TYPE_FD int +#else /* NO SHUT MODE -> USB MODE*/ +#define TYPE_FD struct usb_dev_handle * +#endif /* NO SHUT MODE */ +#else /* WIN32 */ +#define ERROR_FD (NULL) +#define VALID_FD(a) (a!=NULL) +#ifdef SHUT_MODE +#define TYPE_FD serial_handler_t * +#else /* SHUT_MODE */ +#define TYPE_FD hid_dev_handle_t +#endif /* SHUT_MODE */ +#endif /* WIN32 */ + +#endif diff --git a/include/Makefile.am b/include/Makefile.am index 6e4b9666bb..3b8f69a783 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -1,5 +1,5 @@ dist_noinst_HEADERS = attribute.h common.h extstate.h parseconf.h proto.h \ - state.h timehead.h upsconf.h nut_stdint.h nut_platform.h + state.h timehead.h upsconf.h nut_stdint.h nut_platform.h wincompat.h # http://www.gnu.org/software/automake/manual/automake.html#Clean BUILT_SOURCES = nut_version.h diff --git a/include/common.h b/include/common.h index 4de85044a3..dcdccbb7e8 100644 --- a/include/common.h +++ b/include/common.h @@ -36,7 +36,13 @@ #include #include #include +#ifndef WIN32 #include +#else +#include +#include +#include +#endif #include #include @@ -50,6 +56,23 @@ extern "C" { /* *INDENT-ON* */ #endif +#ifdef WIN32 +typedef struct serial_handler_s { + HANDLE handle; + OVERLAPPED io_status; + int overlapped_armed; + + unsigned int vmin_; + unsigned int vtime_; + unsigned int r_binary; + unsigned int w_binary; +} serial_handler_t; + +/* difftime returns erroneous value so we use this macro.*/ +#undef difftime +#define difftime(t1,t0) (double)(t1 - t0) +#endif + extern const char *UPS_VERSION; /* get the syslog ready for us */ @@ -71,13 +94,21 @@ void chroot_start(const char *path); void writepid(const char *name); /* send a signal to another running process */ +#ifndef WIN32 int sendsignal(const char *progname, int sig); +#else +int sendsignal(const char *progname, const char * sig); +#endif int snprintfcat(char *dst, size_t size, const char *fmt, ...) __attribute__ ((__format__ (__printf__, 3, 4))); +#ifndef WIN32 /* open , get the pid, then send it */ int sendsignalfn(const char *pidfn, int sig); +#else +int sendsignalfn(const char *pidfn, const char * sig); +#endif const char *xbasename(const char *file); @@ -120,7 +151,11 @@ char *xstrdup(const char *string); char *rtrim(char *in, const char sep); char* ltrim(char *in, const char sep); +#ifndef WIN32 int select_read(const int fd, void *buf, const size_t buflen, const long d_sec, const long d_usec); +#else +int select_read(serial_handler_t * fd, void *buf, const size_t buflen, const long d_sec, const long d_usec); +#endif int select_write(const int fd, const void *buf, const size_t buflen, const long d_sec, const long d_usec); /* Buffer sizes used for various functions */ @@ -156,4 +191,34 @@ extern int optind; /* *INDENT-ON* */ #endif +#ifdef WIN32 +/* FIXME : this might not be the optimal mapping between syslog and ReportEvent*/ +#define LOG_ERR EVENTLOG_ERROR_TYPE +#define LOG_INFO EVENTLOG_INFORMATION_TYPE +#define LOG_DEBUG EVENTLOG_WARNING_TYPE +#define LOG_NOTICE EVENTLOG_INFORMATION_TYPE +#define LOG_ALERT EVENTLOG_ERROR_TYPE +#define LOG_WARNING EVENTLOG_WARNING_TYPE +#define LOG_CRIT EVENTLOG_ERROR_TYPE +#define LOG_EMERG EVENTLOG_ERROR_TYPE + +#define closelog() + +#define SVCNAME TEXT("Network UPS Tools") +#define EVENTLOG_PIPE_NAME TEXT("nut") +#define UPSMON_PIPE_NAME TEXT("upsmon") +#define UPSD_PIPE_NAME TEXT("upsd") + +char * getfullpath(char * relative_path); +#define PATH_ETC "\\..\\etc" +#define PATH_VAR_RUN "\\..\\var\\run" +#define PATH_SHARE "\\..\\share" +#define PATH_BIN "\\..\\bin" +#define PATH_SBIN "\\..\\sbin" +#endif /* WIN32*/ + +#ifndef HAVE_USLEEP +int __cdecl usleep(unsigned int useconds); +#endif /* HAVE_USLEEP */ + #endif /* NUT_COMMON_H */ diff --git a/include/wincompat.h b/include/wincompat.h new file mode 100644 index 0000000000..5eae270dfc --- /dev/null +++ b/include/wincompat.h @@ -0,0 +1,319 @@ +#ifndef NUT_WINCOMPAT_H +#define NUT_WINCOMPAT_H + +/* + Copyright (C) 2001 Andrew Delpha (delpha@computer.org) + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + + +/* + This header is provided to map Windows system calls to be compatible + with the NUT code. +*/ + +#include "common.h" + +/* This value is defined in the error.h file of the libusb-win32 sources */ +#define ETIMEDOUT 116 + +#define random() rand() +typedef const char * uid_t; +struct passwd { + const char *pw_name; + int pw_uid; /* Fake value, alwaus set to 0 */ +}; + +uid_t getuid(void); +struct passwd *getpwuid(uid_t uid); +char *getpass( const char *prompt); + +#define system(a) win_system(a) +#define sleep(n) Sleep(1000 * n) +char * strtok_r(char *str, const char *delim, char **saveptr); +char * filter_path(const char * source); + +/* Network compatibility */ + +/* This is conditional because read/write are generic in unix, and this will make them network specific */ +#ifdef W32_NETWORK_CALL_OVERRIDE +#define close(h) sktclose(h) +#define read(h,b,s) sktread(h,b,s) +#define write(h,b,s) sktwrite( h ,(char *) b , s ) +#define connect(h,n,l) sktconnect(h,n,l) +#endif + +#ifndef strcasecmp +#define strcasecmp(a,b) _stricmp(a,b) +#endif +#ifndef snprintf +#define snprintf _snprintf +#endif +#ifndef vsnprintf +#define vsnprintf _vsnprintf +#endif + +int sktconnect(int fh, struct sockaddr * name, int len); +int sktread(int fh, char *buf, int size); +int sktwrite(int fh, char *buf, int size); +int sktclose(int fh); + +const char* inet_ntop(int af, const void* src, char* dst, int cnt); + +/* from the MSDN getaddrinfo documentation : */ +#define EAI_AGAIN WSATRY_AGAIN +#define EAI_BADFLAGS WSAEINVAL +#define EAI_FAIL WSANO_RECOVERY +#define EAI_FAMILY WSAEAFNOSUPPORT +#define EAI_MEMORY WSA_NOT_ENOUGH_MEMORY +#define EAI_NONAME WSAHOST_NOT_FOUND +#define EAI_SERVICE WSATYPE_NOT_FOUND +#define EAI_SOCKTYPE WSAESOCKTNOSUPPORT +/* not from MS docs : */ +#define EAI_SYSTEM WSANO_RECOVERY +#define EAFNOSUPPORT WSAEAFNOSUPPORT + +/* "system" function */ +int win_system(const char * command); + +/* syslog compatibility */ + +void syslog(int priority, const char *fmt, ...); + +extern const char * EventLogName; + +/* Signal emulation via named pipe */ +typedef struct pipe_conn_s { + HANDLE handle; + OVERLAPPED overlapped; + char buf[LARGEBUF]; + struct pipe_conn_s *prev; + struct pipe_conn_s *next; +} pipe_conn_t; + +extern pipe_conn_t *pipe_connhead; +extern OVERLAPPED pipe_connection_overlapped; +void pipe_create(const char * pipe_name); +void pipe_connect(); +void pipe_disconnect(pipe_conn_t *conn); +int pipe_ready(pipe_conn_t *conn); +int send_to_named_pipe(const char * pipe_name, const char * data); + +#define COMMAND_FSD "COMMAND_FSD" +#define COMMAND_STOP "COMMAND_STOP" +#define COMMAND_RELOAD "COMMAND_RELOAD" + +/* serial function compatibility */ + +int w32_setcomm ( serial_handler_t * h, int * flags ); +int w32_getcomm ( serial_handler_t * h, int * flags ); +int tcsendbreak (serial_handler_t * sh, int duration); + +typedef unsigned char cc_t; +typedef unsigned int speed_t; +typedef unsigned int tcflag_t; + +#define NCCS 19 +struct termios { + tcflag_t c_iflag; /* input mode flags */ + tcflag_t c_oflag; /* output mode flags */ + tcflag_t c_cflag; /* control mode flags */ + tcflag_t c_lflag; /* local mode flags */ + cc_t c_line; /* line discipline */ + cc_t c_cc[NCCS]; /* control characters */ + speed_t c_ispeed; /* input speed */ + speed_t c_ospeed; /* output speed */ +}; + +serial_handler_t * w32_serial_open (const char *name, int flags); +int w32_serial_close (serial_handler_t * sh); +int w32_serial_write (serial_handler_t * sh, const void *ptr, size_t len); +int w32_serial_read (serial_handler_t * sh, void *ptr, size_t ulen, DWORD timeout); +int tcgetattr (serial_handler_t * sh, struct termios *t); +int tcsetattr (serial_handler_t * sh, int action, const struct termios *t); +int tcflush (serial_handler_t * sh, int queue); +#define HAVE_CFSETISPEED +void cfsetispeed(struct termios * t, speed_t speed); +void cfsetospeed(struct termios * t, speed_t speed); +speed_t cfgetispeed(const struct termios *t); +speed_t cfgetospeed(const struct termios *t); + +#define _POSIX_VDISABLE '\0' + +/* c_cc characters */ +#define VINTR 0 +#define VQUIT 1 +#define VERASE 2 +#define VKILL 3 +#define VEOF 4 +#define VTIME 5 +#define VMIN 6 +#define VSWTC 7 +#define VSTART 8 +#define VSTOP 9 +#define VSUSP 10 +#define VEOL 11 +#define VREPRINT 12 +#define VDISCARD 13 +#define VWERASE 14 +#define VLNEXT 15 +#define VEOL2 16 + +/* c_iflag bits */ +#define IGNBRK 0000001 +#define BRKINT 0000002 +#define IGNPAR 0000004 +#define PARMRK 0000010 +#define INPCK 0000020 +#define ISTRIP 0000040 +#define INLCR 0000100 +#define IGNCR 0000200 +#define ICRNL 0000400 +#define IUCLC 0001000 +#define IXON 0002000 +#define IXANY 0004000 +#define IXOFF 0010000 +#define IMAXBEL 0020000 +#define IUTF8 0040000 + +/* c_oflag bits */ +#define OPOST 0000001 +#define OLCUC 0000002 +#define ONLCR 0000004 +#define OCRNL 0000010 +#define ONOCR 0000020 +#define ONLRET 0000040 +#define OFILL 0000100 +#define OFDEL 0000200 +#define NLDLY 0000400 +#define NL0 0000000 +#define NL1 0000400 +#define CRDLY 0003000 +#define CR0 0000000 +#define CR1 0001000 +#define CR2 0002000 +#define CR3 0003000 +#define TABDLY 0014000 +#define TAB0 0000000 +#define TAB1 0004000 +#define TAB2 0010000 +#define TAB3 0014000 +#define XTABS 0014000 +#define BSDLY 0020000 +#define BS0 0000000 +#define BS1 0020000 +#define VTDLY 0040000 +#define VT0 0000000 +#define VT1 0040000 +#define FFDLY 0100000 +#define FF0 0000000 +#define FF1 0100000 + +/* c_cflag bit meaning */ +#define CBAUD 0010017 +#define B0 0000000 /* hang up */ +#define B50 0000001 +#define B75 0000002 +#define B110 0000003 +#define B134 0000004 +#define B150 0000005 +#define B200 0000006 +#define B300 0000007 +#define B600 0000010 +#define B1200 0000011 +#define B1800 0000012 +#define B2400 0000013 +#define B4800 0000014 +#define B9600 0000015 +#define B19200 0000016 +#define B38400 0000017 +#define EXTA B19200 +#define EXTB B38400 +#define CSIZE 0000060 +#define CS5 0000000 +#define CS6 0000020 +#define CS7 0000040 +#define CS8 0000060 +#define CSTOPB 0000100 +#define CREAD 0000200 +#define PARENB 0000400 +#define PARODD 0001000 +#define HUPCL 0002000 +#define CLOCAL 0004000 +#define CBAUDEX 0010000 +#define BOTHER 0010000 +#define B57600 0010001 +#define B115200 0010002 +#define B230400 0010003 +#define B460800 0010004 +#define B500000 0010005 +#define B576000 0010006 +#define B921600 0010007 +#define B1000000 0010010 +#define B1152000 0010011 +#define B1500000 0010012 +#define B2000000 0010013 +#define B2500000 0010014 +#define B3000000 0010015 +#define B3500000 0010016 +#define B4000000 0010017 +#define CIBAUD 002003600000 /* input baud rate */ +#define CMSPAR 010000000000 /* mark or space (stick) parity */ +#define CRTSCTS 020000000000 /* flow control */ + +#define IBSHIFT 16 /* Shift from CBAUD to CIBAUD */ + +/* c_lflag bits */ +#define ISIG 0000001 +#define ICANON 0000002 +#define XCASE 0000004 +#define ECHO 0000010 +#define ECHOE 0000020 +#define ECHOK 0000040 +#define ECHONL 0000100 +#define NOFLSH 0000200 +#define TOSTOP 0000400 +#define ECHOCTL 0001000 +#define ECHOPRT 0002000 +#define ECHOKE 0004000 +#define FLUSHO 0010000 +#define PENDIN 0040000 +#define IEXTEN 0100000 + +/* tcflow() and TCXONC use these */ +#define TCOOFF 0 +#define TCOON 1 +#define TCIOFF 2 +#define TCION 3 + +/* tcflush() and TCFLSH use these */ +#define TCIFLUSH 0 +#define TCOFLUSH 1 +#define TCIOFLUSH 2 + +/* tcsetattr uses these */ +#define TCSANOW 0 +#define TCSADRAIN 1 +#define TCSAFLUSH 2 + +#define TIOCM_DTR 0x0001 +#define TIOCM_RTS 0x0002 +#define TIOCM_ST 0x0004 +#define TIOCM_CTS MS_CTS_ON /* 0x0010*/ +#define TIOCM_DSR MS_DSR_ON /* 0x0020*/ +#define TIOCM_RNG MS_RING_ON /*0x0040*/ +#define TIOCM_CD MS_RLSD_ON /*0x0080*/ +#endif diff --git a/m4/nut_check_libusb.m4 b/m4/nut_check_libusb.m4 index b2bb0abd00..efbf18690c 100644 --- a/m4/nut_check_libusb.m4 +++ b/m4/nut_check_libusb.m4 @@ -61,6 +61,8 @@ if test -z "${nut_have_libusb_seen}"; then ], []) AC_MSG_RESULT([${LIBS}]) + AC_SEARCH_LIBS(regcomp, regex) + dnl check if libusb is usable AC_CHECK_HEADERS(usb.h, [nut_have_libusb=yes], [nut_have_libusb=no], [AC_INCLUDES_DEFAULT]) AC_CHECK_FUNCS(usb_init, [], [nut_have_libusb=no]) diff --git a/m4/nut_check_socketlib.m4 b/m4/nut_check_socketlib.m4 new file mode 100644 index 0000000000..65df958516 --- /dev/null +++ b/m4/nut_check_socketlib.m4 @@ -0,0 +1,41 @@ +dnl Copyright (C) 2008-2010 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +dnl NUT_CHECK_SOCKETLIB +dnl Determines the library to use for socket functions. +dnl Sets and AC_SUBSTs NETLIBS. + +dnl This code comes from gnulib trunk (4 Nov. 2010). + +AC_DEFUN([NUT_CHECK_SOCKETLIB], +[ + NUT_PREREQ_SYS_H_SOCKET dnl for HAVE_WINSOCK2_H + NETLIBS= + if test $HAVE_WINSOCK2_H = 1; then + dnl Native Windows API (not Cygwin). + AC_CACHE_CHECK([if we need to call WSAStartup in winsock2.h and -lws2_32], + [nut_cv_func_wsastartup], [ + nut_save_LIBS="$LIBS" + LIBS="$LIBS -lws2_32" + AC_LINK_IFELSE([AC_LANG_PROGRAM([[ +#ifdef HAVE_WINSOCK2_H +# include +#endif]], [[ + WORD wVersionRequested = MAKEWORD(1, 1); + WSADATA wsaData; + int err = WSAStartup(wVersionRequested, &wsaData); + WSACleanup ();]])], + nut_cv_func_wsastartup=yes, nut_cv_func_wsastartup=no) + LIBS="$nut_save_LIBS" + ]) + if test "$nut_cv_func_wsastartup" = "yes"; then + AC_DEFINE([WINDOWS_SOCKETS], [1], [Define if WSAStartup is needed.]) + NETLIBS='-lws2_32' + fi + fi + AC_SUBST([NETLIBS]) + + AM_CONDITIONAL([HAVE_WINDOWS_SOCKETS], [test "$nut_cv_func_wsastartup" = "yes"]) +]) diff --git a/m4/nut_type_socklen_t.m4 b/m4/nut_type_socklen_t.m4 index 0cbf4b3e30..21e3b7a8ba 100644 --- a/m4/nut_type_socklen_t.m4 +++ b/m4/nut_type_socklen_t.m4 @@ -1,43 +1,95 @@ dnl Check for socklen_t: historically on BSD it is an int, and in dnl POSIX 1g it is a type of its own, but some platforms use different -dnl types for the argument to getsockopt, getpeername, etc. So we -dnl have to test to find something that will work. +dnl types for the argument to getsockopt, getpeername, etc.: +dnl HP-UX 10.20, IRIX 6.5, Interix 3.5, BeOS. +dnl So we have to test to find something that will work. -dnl This code gets around. This instance came from rsync 2.5.6. +dnl Copyright (C) 2005, 2006, 2007, 2009, 2010 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +dnl From Albert Chin, Windows fixes from Simon Josefsson. + +dnl On mingw32, socklen_t is in ws2tcpip.h ('int'), so we try to find +dnl it there first. That file is included by gnulib sys_socket.in.h, which +dnl all users of this module should include. Cygwin must not include +dnl ws2tcpip.h. + +dnl This code gets around. This instance came from gnulib trunk (21 Oct. 2010). AC_DEFUN([NUT_TYPE_SOCKLEN_T], -[ - AC_CHECK_TYPE([socklen_t], ,[ - AC_MSG_CHECKING([for socklen_t equivalent]) + [ + NUT_PREREQ_SYS_H_SOCKET + AC_CHECK_TYPE([socklen_t], , + [AC_MSG_CHECKING([for socklen_t equivalent]) AC_CACHE_VAL([nut_cv_socklen_t_equiv], - [ - # Systems have either "struct sockaddr *" or + [# Systems have either "struct sockaddr *" or # "void *" as the second argument to getpeername nut_cv_socklen_t_equiv= for arg2 in "struct sockaddr" void; do - for t in int size_t unsigned long "unsigned long"; do - AC_TRY_COMPILE([ -#include -#include + for t in int size_t "unsigned int" "long int" "unsigned long int"; do + AC_COMPILE_IFELSE([AC_LANG_PROGRAM( + [[#include + #ifdef HAVE_SYS_SOCKET_H + #include + #endif + #ifdef HAVE_WS2TCPIP_H + #include + #endif - int getpeername (int, $arg2 *, $t *); - ],[ - $t len; - getpeername(0,0,&len); - ],[ - nut_cv_socklen_t_equiv="$t" - break - ]) - done + int getpeername (int, $arg2 *, $t *);]], + [[$t len; + getpeername (0, 0, &len);]])], + [nut_cv_socklen_t_equiv="$t"]) + test "$nut_cv_socklen_t_equiv" != "" && break + done + test "$nut_cv_socklen_t_equiv" != "" && break done - - if test "x$nut_cv_socklen_t_equiv" = x; then - AC_MSG_ERROR([Cannot find a type to use in place of socklen_t]) - fi ]) - AC_MSG_RESULT($nut_cv_socklen_t_equiv) - AC_DEFINE_UNQUOTED(socklen_t, $nut_cv_socklen_t_equiv, - [type to use in place of socklen_t if not defined])], - [#include -#include ]) + if test "$nut_cv_socklen_t_equiv" = ""; then + AC_MSG_ERROR([Cannot find a type to use in place of socklen_t]) + fi + AC_MSG_RESULT([$nut_cv_socklen_t_equiv]) + AC_DEFINE_UNQUOTED([socklen_t], [$nut_cv_socklen_t_equiv], + [type to use in place of socklen_t if not defined])], + [#include + #ifdef HAVE_SYS_SOCKET_H + #include + #endif + #ifdef HAVE_WS2TCPIP_H + #include + #endif]) +]) + +AC_DEFUN([NUT_PREREQ_SYS_H_SOCKET], +[ + dnl Check prerequisites of the replacement. + AC_CHECK_HEADERS_ONCE([sys/socket.h]) + if test $ac_cv_header_sys_socket_h = yes; then + HAVE_SYS_SOCKET_H=1 + HAVE_WS2TCPIP_H=0 + HAVE_WINSOCK2_H=0 + else + HAVE_SYS_SOCKET_H=0 + dnl We cannot use AC_CHECK_HEADERS_ONCE here, because that would make + dnl the check for those headers unconditional; yet cygwin reports + dnl that the headers are present but cannot be compiled (since on + dnl cygwin, all socket information should come from sys/socket.h). + AC_CHECK_HEADERS([ws2tcpip.h]) + if test $ac_cv_header_ws2tcpip_h = yes; then + HAVE_WS2TCPIP_H=1 + else + HAVE_WS2TCPIP_H=0 + fi + AC_CHECK_HEADERS([winsock2.h]) + if test "$ac_cv_header_winsock2_h" = yes; then + HAVE_WINSOCK2_H=1 + else + HAVE_WINSOCK2_H=0 + fi + fi + AC_SUBST([HAVE_WINSOCK2_H]) + AC_SUBST([HAVE_SYS_SOCKET_H]) + AC_SUBST([HAVE_WS2TCPIP_H]) ]) diff --git a/scripts/Makefile.am b/scripts/Makefile.am index b6dde246f0..a59e89d674 100644 --- a/scripts/Makefile.am +++ b/scripts/Makefile.am @@ -24,4 +24,4 @@ upower/95-upower-hid.rules \ Windows/halt.c \ Windows/Makefile -SUBDIRS = augeas hal hotplug python systemd udev Solaris +SUBDIRS = augeas hal hotplug python systemd udev Solaris Windows diff --git a/scripts/Windows/.gitignore b/scripts/Windows/.gitignore new file mode 100644 index 0000000000..381b62880a --- /dev/null +++ b/scripts/Windows/.gitignore @@ -0,0 +1,5 @@ +.wininit.c.swp +*.exe +winevent.h +winevent.rc +MSG00409.bin diff --git a/scripts/Windows/DriverInstaller/README.txt b/scripts/Windows/DriverInstaller/README.txt new file mode 100644 index 0000000000..9eec3d7a82 --- /dev/null +++ b/scripts/Windows/DriverInstaller/README.txt @@ -0,0 +1,3 @@ +To easily compile wdi-simple.exe, unzip a copy of libwdi on your disk. +Then set it up to be able to build following http://sourceforge.net/apps/mediawiki/libwdi/index.php?title=Install +Then copy wdi-simple.c and nutscan-usb.h into the "examples" subdirectory of libwdi directory. Finally run ddk_build.cmd from the root of the libwdi directory. diff --git a/scripts/Windows/DriverInstaller/wdi-simple.c b/scripts/Windows/DriverInstaller/wdi-simple.c new file mode 100644 index 0000000000..ef83b9ac39 --- /dev/null +++ b/scripts/Windows/DriverInstaller/wdi-simple.c @@ -0,0 +1,122 @@ +/* + wdi-simple.c: Console Driver Installer for NUT USB devices + Copyright (c) 2010 Pete Batard + Copyright (c) 2011 Frederic Bohe + + 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 +*/ + +#include +#include +#include +#ifdef _MSC_VER +#include "getopt/getopt.h" +#else +#include +#endif +#include "libwdi.h" +#include "nutscan-usb.h" + +#define oprintf(...) do {if (!opt_silent) printf(__VA_ARGS__);} while(0) + +#define DESC "NUT USB driver" +#define INF_NAME "usb_device.inf" +#define DEFAULT_DIR "usb_driver" + + +int __cdecl main(int argc, char** argv) +{ + char desc[128] = DESC; + struct wdi_device_info *ldev, *ldev_start, dev = {NULL, 0, 0, false, 0, DESC, NULL, NULL, NULL}; + struct wdi_options_create_list ocl = { 0 }; + struct wdi_options_prepare_driver opd = { 0 }; + struct wdi_options_install_driver oid = { 0 }; + int c, r; + int opt_silent = 0, opt_extract = 0, log_level = WDI_LOG_LEVEL_WARNING; +// int opt_silent = 0, opt_extract = 0, log_level = WDI_LOG_LEVEL_DEBUG; + char *inf_name = INF_NAME; + char *ext_dir = DEFAULT_DIR; + bool matching_device_found; + int index = 0; + + ocl.list_all = true; + ocl.list_hubs = true; + ocl.trim_whitespaces = true; + opd.driver_type = WDI_LIBUSB0; +// opd.driver_type = WDI_WINUSB; + + wdi_set_log_level(log_level); + + oprintf("NUT UPS driver installer.\n"); + oprintf("-------------------------\n\n"); + oprintf("Searching for known UPS...\n"); + + // Try to match against a plugged device + matching_device_found = false; + if (wdi_create_list(&ldev, &ocl) == WDI_SUCCESS) { + r = WDI_SUCCESS; + ldev_start = ldev; + while( usb_device_table[index].vendorID != 0xFFFF || usb_device_table[index].productID != 0xFFFF) { + dev.next = NULL; + dev.vid = usb_device_table[index].vendorID; + dev.pid = usb_device_table[index].productID; + dev.is_composite = false; + dev.mi = 0; + dev.desc = desc; + dev.driver = NULL; + dev.device_id = NULL; + dev.hardware_id = NULL; +// oprintf("NUT device : vid : %0X - pid : %0X\n",dev.vid, dev.pid); + + for (ldev = ldev_start; (ldev != NULL) && (r == WDI_SUCCESS); ldev = ldev->next) { +// oprintf("trying vid : %0X - pid : %0X\n",ldev->vid, ldev->pid); + if ( (ldev->vid == dev.vid) && (ldev->pid == dev.pid) && (ldev->mi == dev.mi) ) { + oprintf("Found UPS : vendor ID = %0X - Product ID = %0X\n",ldev->vid, ldev->pid, ldev->mi); + dev.hardware_id = ldev->hardware_id; + dev.device_id = ldev->device_id; + matching_device_found = true; + + oprintf("Extracting driver files...\n"); + r = wdi_prepare_driver(&dev, ext_dir, inf_name, &opd); + oprintf(" %s\n", wdi_strerror(r)); + if ((r != WDI_SUCCESS) || (opt_extract)) + return r; + + oprintf(" %s: ", dev.hardware_id); + fflush(stdout); + oprintf("Installing driver\n"); + r = wdi_install_driver(&dev, ext_dir, inf_name, &oid); + oprintf("%s\n", wdi_strerror(r)); + if( r == WDI_SUCCESS ) { + oprintf("You should now unplug and re-plug your device to finish driver's installation.\nHit enter when it's done.\n"); + } + else { + oprintf("An error occured while installing driver.\nTry installing libUSB manually.\nHit enter to continue\n"); + } + getc(stdin); + } + } + index++; + } + } + + // No plugged USB device matches + if (!matching_device_found) { + oprintf("No known UPS device found.\nTry installing libUSB manually.\nHit enter to continue\n"); + getc(stdin); + } + + return r; +} diff --git a/scripts/Windows/Installer/.gitignore b/scripts/Windows/Installer/.gitignore new file mode 100644 index 0000000000..5466afe66c --- /dev/null +++ b/scripts/Windows/Installer/.gitignore @@ -0,0 +1,4 @@ +NUT-Installer.msi +NUT-Installer.wixobj +NUT-Installer.wixpdb +log.txt diff --git a/scripts/Windows/Installer/BuildInstaller.bat b/scripts/Windows/Installer/BuildInstaller.bat new file mode 100644 index 0000000000..d05fdcedf4 --- /dev/null +++ b/scripts/Windows/Installer/BuildInstaller.bat @@ -0,0 +1,35 @@ +::This script can be used to Create and Build NUT installer using WiX. + +@echo off + +SET BATDIR=%~dp0 +cd /d %BATDIR% + +SET MSYS_BIN_DIR=c:\mingw\msys\1.0\bin\ +SET MINGW_BIN_DIR=c:\mingw\bin\ +SET NUT-XML-FILE=NUT-Installer.xml +SET wixobjName=NUT-Installer.wixobj +SET msiPackageName=NUT-Installer.msi + +%MSYS_BIN_DIR%unix2dos.exe ../../../conf/upssched.conf.sample + +echo copy DLL files from MSYS +copy /Y %MSYS_BIN_DIR%msys-1.0.dll .\ImageFiles\Others +copy /Y %MSYS_BIN_DIR%msys-crypto-1.0.0.dll .\ImageFiles\Others +copy /Y %MSYS_BIN_DIR%msys-ssl-1.0.0.dll .\ImageFiles\Others +copy /Y %MSYS_BIN_DIR%msys-regex-1.dll .\ImageFiles\Others + +REM use "candle.exe" to create the "object" file +candle.exe "%NUT-XML-FILE%" -out "%wixobjName%" >"log.txt" +@echo ========================================================= +@echo Please wait as MSI package creation in progress... + +@echo off +REM use "light.exe" to create the "MSi" package +light.exe "%wixobjName%" -out "%msiPackageName%" >>"log.txt" + +@echo ========================================================= +@echo MSI package "%msiPackageName%" complete +@echo ========================================================= +@echo Check output file "log.txt" for status of completion... +@echo ========================================================= diff --git a/scripts/Windows/Installer/ImageFiles/Images/NUT_wix_horizontal.bmp b/scripts/Windows/Installer/ImageFiles/Images/NUT_wix_horizontal.bmp new file mode 100644 index 0000000000..b0b066336d Binary files /dev/null and b/scripts/Windows/Installer/ImageFiles/Images/NUT_wix_horizontal.bmp differ diff --git a/scripts/Windows/Installer/ImageFiles/Images/NUT_wix_vertical.bmp b/scripts/Windows/Installer/ImageFiles/Images/NUT_wix_vertical.bmp new file mode 100644 index 0000000000..c930c05409 Binary files /dev/null and b/scripts/Windows/Installer/ImageFiles/Images/NUT_wix_vertical.bmp differ diff --git a/scripts/Windows/Installer/ImageFiles/Others/.gitignore b/scripts/Windows/Installer/ImageFiles/Others/.gitignore new file mode 100644 index 0000000000..7771adbbb2 --- /dev/null +++ b/scripts/Windows/Installer/ImageFiles/Others/.gitignore @@ -0,0 +1,14 @@ +libexpat-1.dll +libgcc_s_dw2-1.dll +libiconv-2.dll +libintl-8.dll +libltdl-7.dll +libneon-27.dll +libnetsnmp-30.dll +libregex-1.dll +libz-1.dll +msys-1.0.dll +msys-crypto-1.0.0.dll +msys-regex-1.dll +msys-ssl-1.0.0.dll +pthreadgc2.dll diff --git a/scripts/Windows/Installer/ImageFiles/Others/StartService.bat b/scripts/Windows/Installer/ImageFiles/Others/StartService.bat new file mode 100644 index 0000000000..5b8333c6f9 --- /dev/null +++ b/scripts/Windows/Installer/ImageFiles/Others/StartService.bat @@ -0,0 +1 @@ +net start "Network UPS Tools" \ No newline at end of file diff --git a/scripts/Windows/Installer/ImageFiles/Others/StopService.bat b/scripts/Windows/Installer/ImageFiles/Others/StopService.bat new file mode 100644 index 0000000000..718da56a85 --- /dev/null +++ b/scripts/Windows/Installer/ImageFiles/Others/StopService.bat @@ -0,0 +1 @@ +net stop "Network UPS Tools" \ No newline at end of file diff --git a/scripts/Windows/Installer/ImageFiles/emptyDir/cgi-bin/temp.txt b/scripts/Windows/Installer/ImageFiles/emptyDir/cgi-bin/temp.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/scripts/Windows/Installer/ImageFiles/emptyDir/html/temp.txt b/scripts/Windows/Installer/ImageFiles/emptyDir/html/temp.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/scripts/Windows/Installer/ImageFiles/emptyDir/include/temp.txt b/scripts/Windows/Installer/ImageFiles/emptyDir/include/temp.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/scripts/Windows/Installer/ImageFiles/emptyDir/man1/temp.txt b/scripts/Windows/Installer/ImageFiles/emptyDir/man1/temp.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/scripts/Windows/Installer/ImageFiles/emptyDir/man3/temp.txt b/scripts/Windows/Installer/ImageFiles/emptyDir/man3/temp.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/scripts/Windows/Installer/ImageFiles/emptyDir/pkgconfig/temp.txt b/scripts/Windows/Installer/ImageFiles/emptyDir/pkgconfig/temp.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/scripts/Windows/Installer/ImageFiles/emptyDir/run/temp.txt b/scripts/Windows/Installer/ImageFiles/emptyDir/run/temp.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/scripts/Windows/Installer/ImageFiles/icons/New.ico b/scripts/Windows/Installer/ImageFiles/icons/New.ico new file mode 100644 index 0000000000..27881dfe97 Binary files /dev/null and b/scripts/Windows/Installer/ImageFiles/icons/New.ico differ diff --git a/scripts/Windows/Installer/ImageFiles/icons/Up.ico b/scripts/Windows/Installer/ImageFiles/icons/Up.ico new file mode 100644 index 0000000000..86f6b5a847 Binary files /dev/null and b/scripts/Windows/Installer/ImageFiles/icons/Up.ico differ diff --git a/scripts/Windows/Installer/ImageFiles/icons/completi.ico b/scripts/Windows/Installer/ImageFiles/icons/completi.ico new file mode 100644 index 0000000000..93a95a1b19 Binary files /dev/null and b/scripts/Windows/Installer/ImageFiles/icons/completi.ico differ diff --git a/scripts/Windows/Installer/ImageFiles/icons/custicon.ico b/scripts/Windows/Installer/ImageFiles/icons/custicon.ico new file mode 100644 index 0000000000..878d3ba54b Binary files /dev/null and b/scripts/Windows/Installer/ImageFiles/icons/custicon.ico differ diff --git a/scripts/Windows/Installer/ImageFiles/icons/exclamic.ico b/scripts/Windows/Installer/ImageFiles/icons/exclamic.ico new file mode 100644 index 0000000000..906ce3246d Binary files /dev/null and b/scripts/Windows/Installer/ImageFiles/icons/exclamic.ico differ diff --git a/scripts/Windows/Installer/ImageFiles/icons/info.ico b/scripts/Windows/Installer/ImageFiles/icons/info.ico new file mode 100644 index 0000000000..7e0ff7f14c Binary files /dev/null and b/scripts/Windows/Installer/ImageFiles/icons/info.ico differ diff --git a/scripts/Windows/Installer/ImageFiles/icons/insticon.ico b/scripts/Windows/Installer/ImageFiles/icons/insticon.ico new file mode 100644 index 0000000000..94753ac296 Binary files /dev/null and b/scripts/Windows/Installer/ImageFiles/icons/insticon.ico differ diff --git a/scripts/Windows/Installer/ImageFiles/icons/powernut_Stop.ico b/scripts/Windows/Installer/ImageFiles/icons/powernut_Stop.ico new file mode 100644 index 0000000000..afe5ab659f Binary files /dev/null and b/scripts/Windows/Installer/ImageFiles/icons/powernut_Stop.ico differ diff --git a/scripts/Windows/Installer/ImageFiles/icons/powernut_logo.ico b/scripts/Windows/Installer/ImageFiles/icons/powernut_logo.ico new file mode 100644 index 0000000000..05d3b312f0 Binary files /dev/null and b/scripts/Windows/Installer/ImageFiles/icons/powernut_logo.ico differ diff --git a/scripts/Windows/Installer/ImageFiles/icons/removico.ico b/scripts/Windows/Installer/ImageFiles/icons/removico.ico new file mode 100644 index 0000000000..097cafe274 Binary files /dev/null and b/scripts/Windows/Installer/ImageFiles/icons/removico.ico differ diff --git a/scripts/Windows/Installer/ImageFiles/icons/repairic.ico b/scripts/Windows/Installer/ImageFiles/icons/repairic.ico new file mode 100644 index 0000000000..6fb68610c9 Binary files /dev/null and b/scripts/Windows/Installer/ImageFiles/icons/repairic.ico differ diff --git a/scripts/Windows/Installer/NUT-Installer.xml b/scripts/Windows/Installer/NUT-Installer.xml new file mode 100644 index 0000000000..dd863c44ef --- /dev/null +++ b/scripts/Windows/Installer/NUT-Installer.xml @@ -0,0 +1,2078 @@ + + + + + + + + + + + + + + + + + + + + + + + + If the automatic USB driver installation fails, you can try to install a driver manually. For this go to : + +https://sourceforge.net/projects/libusb-win32/files/libusb-win32-releases/ + +After installling libusb-win32, run libUSB's Inf Wizard and choose your device. + + + Click Next to continue [Wizard]. + + + + + + If you use a USB UPS, please plug it in now, so that we can try to install the relevant driver. + + + + + + + + + + + + + + + + + + + [DlgTitleFont][ProductName] installs the following libraries, location of licenses are - + + + Click Next to continue [Wizard]. + + + + 1. msys-1.0.dll - http://sourceforge.net/projects/mingw/files/MSYS/BaseSystem/msys-core/msys-1.0.16-1/ + + + 2. msys-ssl-1.0.0.dll and msys-crypto-1.0.0.dll - http://sourceforge.net/projects/mingw/files/MSYS/openssl/openssl-1.0.0-1/ + + + 3. libregex-1.dll - http://sourceforge.net/projects/mingw/files/MSYS/BaseSystem/regex/regex-1.20090805-2/ + + + + + + + + + + + + + Click the Finish button to exit the [Wizard]. + + + {\VerdanaBold13}Completing the [ProductName] [Wizard] + + + + + + + + + + + + {\VerdanaBold13}[ProductName] [Wizard] ended prematurely + + + [ProductName] setup ended prematurely because of an error. Your system has not been modified. To install this program at a later time, please run the installation again. + + + Click the Finish button to exit the [Wizard]. + + + + + + + + + + Please wait while the [Wizard] prepares to guide you through the installation. + + + {\VerdanaBold13}Welcome to the [ProductName] [Wizard] + + + + + + + + + + + + + + + + + + Please wait while the [Wizard] [Progress2] [ProductName]. This may take several minutes. + + + + [DlgTitleFont][Progress1] [ProductName] + + + + + + + + + + + + + + + + + + + + {\VerdanaBold13}[ProductName] [Wizard] was interrupted + + + [ProductName] setup was interrupted. Your system has not been modified. To install this program at a later time, please run the installation again. + + + Click the Finish button to exit the [Wizard]. + + + + + + + + + + + + + + + + + + + + + + + + + + + Browse to the destination folder + + + [DlgTitleFont]Change current destination folder + + + + + + + + + + + + + + + + Are you sure you want to cancel [ProductName] installation? + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The following applications are using files that need to be updated by this setup. Close these applications and then click Retry to continue the installation or Cancel to exit it. + + + + Some files that need to be updated are currently in use. + + + [DlgTitleFont]Files in Use + + + + + + + + + + + "Yes"]]> + + + + + + + + + {\rtf1\ansi\ansicpg1252\deff0\deftab720 + {\fonttbl{\f0\froman\fprq2 Times New Roman;}} + {\colortbl\red0\green0\blue0;} + \deflang1033\horzdoc{\*\fchars }{\*\lchars } + \pard\plain\f0\fs18 \par + GNU GENERAL PUBLIC LICENSE \par + Version 2, June 1991 + \par \par +Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. \par + + Preamble \par +The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. \par + +\par +When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + \par +To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + \par + \par +For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their rights. + \par + \par +We protect your rights with two steps: +\par (1) copyright the software, and +\par (2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + \par + \par +Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + \par + \par +Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + \par + \par +The precise terms and conditions for copying, distribution and +modification follow. + \par + \par + GNU GENERAL PUBLIC LICENSE + \par \par + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + \par \par + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + \par \par + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + \par \par + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + \par \par +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + \par \par + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + \par \par + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + \par \par + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + \par \par + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + \par \par +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + \par \par +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + \par \par +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + \par \par + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + \par \par + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + \par \par + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + \par \par + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + \par \par +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + \par \par +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + \par \par + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + \par \par + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + \par \par + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + \par \par + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + \par \par +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + \par \par +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + \par \par +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + \par \par + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + \par \par + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + \par \par +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + \par \par + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + \par \par + NO WARRANTY + \par \par + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + \par \par + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + \par \par + END OF TERMS AND CONDITIONS + \par \par + How to Apply These Terms to Your New Programs + \par \par + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + \par \par + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + \par \par + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + \par \par + 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. + \par \par + 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. + \par \par + 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., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + \par \par +Also add information on how to contact you by electronic and paper mail. + \par \par +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + \par \par + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + \par +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + \par \par +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + \par \par + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + \par \par + <signature of Ty Coon>, 1 April 1989 + \par + Ty Coon, President of Vice + \par \par +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. + \par \par + + } + + + + + + + + [DlgTitleFont]Re&pair + + + + + + + + + [DlgTitleFont]&Remove + + + + + + + + + + + + + + + + + + + Select the operation you wish to perform. + + + [DlgTitleFont]Repair or Remove installation + + + Removes [ProductName] from your computer. + + + Repairs errors in the most recent installation state - fixes missing or corrupt files, shortcuts and registry entries. + + + + + + + + + + + + + + + The [Wizard] will allow you to change the way [ProductName] features are installed on your computer or even to remove [ProductName] from your computer. Click Next to continue or Cancel to exit the [Wizard]. + + + {\VerdanaBold13}Welcome to the [ProductName] [Wizard] + + + + + 1]]> + + + + + + + + + + + The [Wizard] will complete the installation of [ProductName] on your computer. Click Install to continue or Cancel to exit the [Wizard]. + + + {\VerdanaBold13}Resuming the [ProductName] [Wizard] + + + + + 1]]> + + + + + + + + + + + + + Click Install to begin the installation. If you want to review or change any of your installation settings, click Back. Click Cancel to exit the wizard. + + + + The [Wizard] is ready to begin the [InstallMode] installation + + + [DlgTitleFont]Ready to Install + + + + + + + + + 1]]> + 1]]> + + + + + + + + + + Click Remove to remove [ProductName] from your computer. If you want to review or change any of your installation settings, click Back. Click Cancel to exit the wizard. + + + + You have chosen to remove the program from your computer. + + + [DlgTitleFont]Remove [ProductName] + + + + + + 1]]> + 1]]> + 1]]> + + + + + + + + + + + + + Click Repair to repair the installation of [ProductName]. If you want to review or change any of your installation settings, click Back. Click Cancel to exit the wizard. + + + + The [Wizard] is ready to begin the repair of [ProductName]. + + + [DlgTitleFont]Repair [ProductName] + + + + + + + + + + + + + + + + The [Wizard] will install [ProductName] on your computer. Click Next to continue or Cancel to exit the [Wizard]. + + + {\VerdanaBold13}Welcome to the [ProductName] [Wizard] + + + + + + + + + + + bytes + GB + KB + MB + Entire feature will be unavailable + Feature will be installed when required + Entire feature will be installed to run from CD + Entire feature will be installed on local hard drive + Entire feature will be installed to run from network + Will be installed to run from CD + Will be installed on local hard drive + Will be installed to run from network + Gathering required information... + This feature will remain uninstalled + This feature will be set to be installed when required + This feature will be installed to run from CD + This feature will be installed on the local hard drive + This feature will be installed to run from the network + This feature will become unavailable + Will be installed when required + This feature will be available to run from CD + This feature will be installed on your local hard drive + This feature will be available to run from the network + This feature will be uninstalled completely, you won't be able to run it from CD + This feature will change from run from CD state to set to be installed when required + This feature will remain to be run from CD + This feature will change from run from CD state to be installed on the local hard drive + This feature frees up [1] on your hard drive. + This feature requires [1] on your hard drive. + Compiling cost for this feature... + This feature will be completely removed + This feature will be removed from your local hard drive, but will be set to be installed when required + This feature will be removed from your local hard drive, but will be still available to run from CD + This feature will remain on you local hard drive + This feature will be removed from your local hard drive, but will be still available to run from the network + This feature will be uninstalled completely, you won't be able to run it from the network + This feature will change from run from network state to set to be installed when required + This feature will change from run from network state to be installed on the local hard drive + This feature will remain to be run from the network + This feature frees up [1] on your hard drive. It has [2] of [3] subfeatures selected. The subfeatures free up [4] on your hard drive. + This feature frees up [1] on your hard drive. It has [2] of [3] subfeatures selected. The subfeatures require [4] on your hard drive. + This feature requires [1] on your hard drive. It has [2] of [3] subfeatures selected. The subfeatures free up [4] on your hard drive. + This feature requires [1] on your hard drive. It has [2] of [3] subfeatures selected. The subfeatures require [4] on your hard drive. + Time remaining: {[1] minutes }{[2] seconds} + Available + Difference + Required + Disk Size + Volume + Validating install + Copying new files + Copying network install files + Computing space requirements + Computing space requirements + Computing space requirements + Creating shortcuts + Publishing Qualified Components + Publishing Product Features + Publishing product information + Registering Class servers + Registering extension servers + Registering MIME info + Registering program identifiers + Allocating registry space + Searching for installed applications + Binding executables + Searching for qualifying products + Creating folders + Deleting services + Creating duplicate files + Searching for related applications + Installing ODBC components + Installing new services + Evaluating launch conditions + Migrating feature states from related applications + Moving files + Patching files + Updating component registration + Registering COM+ Applications and Components + Registering fonts + Registering product + Registering type libraries + Registering user + Removing duplicated files + Updating environment strings + Removing applications + Removing files + Removing folders + Removing INI files entries + Removing ODBC components + Removing system registry values + Removing shortcuts + Searching for qualifying products + Registering modules + Unregistering modules + Initializing ODBC directories + Starting services + Stopping services + Unpublishing Qualified Components + Unpublishing Product Features + Unregister Class servers + Unregistering COM+ Applications and Components + Unregistering extension servers + Unregistering fonts + Unregistering MIME info + Unregistering program identifiers + Unregistering type libraries + Updating environment strings + Writing INI files values + Writing system registry values + Advertising application + Generating script operations for action: + Installing system catalog + Publishing assembly information + Unpublishing assembly information + Rolling back action: + Removing backup files + Removing moved files + Unpublishing product information + {{Fatal error: }} + {{Error [1]. }} + Warning [1]. + + Info [1]. + The installer has encountered an unexpected error installing this package. This may indicate a problem with this package. The error code is [1]. {{The arguments are: [2], [3], [4]}} + + {{Disk full: }} + Action [Time]: [1]. [2] + [ProductName] + {[2]}{, [3]}{, [4]} + Message type: [1], Argument: [2] + === Logging started: [Date] [Time] === + === Logging stopped: [Date] [Time] === + Action start [Time]: [1]. + Action ended [Time]: [1]. Return value [2]. + Time remaining: {[1] minutes }{[2] seconds} + Out of memory. Shut down other applications before retrying. + Installer is no longer responding. + Installer stopped prematurely. + Please wait while Windows configures [ProductName] + Gathering required information... + Removing older versions of this application... + Preparing to remove older versions of this application... + {[ProductName] }Setup completed successfully. + {[ProductName] }Setup failed. + Error reading from file: [2]. {{ System error [3].}} Verify that the file exists and that you can access it. + Cannot create the file '[2]'. A directory with this name already exists. Cancel the install and try installing to a different location. + Please insert the disk: [2] + The installer has insufficient privileges to access this directory: [2]. The installation cannot continue. Log on as administrator or contact your system administrator. + Error writing to file: [2]. Verify that you have access to that directory. + Error reading from file [2]. {{ System error [3].}} Verify that the file exists and that you can access it. + Another application has exclusive access to the file '[2]'. Please shut down all other applications, then click Retry. + There is not enough disk space to install this file: [2]. Free some disk space and click Retry, or click Cancel to exit. + Source file not found: [2]. Verify that the file exists and that you can access it. + Error reading from file: [3]. {{ System error [2].}} Verify that the file exists and that you can access it. + Error writing to file: [3]. {{ System error [2].}} Verify that you have access to that directory. + Source file not found{{(cabinet)}}: [2]. Verify that the file exists and that you can access it. + Cannot create the directory '[2]'. A file with this name already exists. Please rename or remove the file and click retry, or click Cancel to exit. + The volume [2] is currently unavailable. Please select another. + The specified path '[2]' is unavailable. + Unable to write to the specified folder: [2]. + A network error occurred while attempting to read from the file: [2] + An error occurred while attempting to create the directory: [2] + A network error occurred while attempting to create the directory: [2] + A network error occurred while attempting to open the source file cabinet: [2] + The specified path is too long: [2] + The Installer has insufficient privileges to modify this file: [2]. + A portion of the folder path '[2]' is invalid. It is either empty or exceeds the length allowed by the system. + The folder path '[2]' contains words that are not valid in folder paths. + The folder path '[2]' contains an invalid character. + '[2]' is not a valid short file name. + Error getting file security: [3] GetLastError: [2] + Invalid Drive: [2] + Error applying patch to file [2]. It has probably been updated by other means, and can no longer be modified by this patch. For more information contact your patch vendor. {{System Error: [3]}} + A file that is required cannot be installed because the cabinet file [2] is not digitally signed. This may indicate that the cabinet file is corrupt. + A file that is required cannot be installed because the cabinet file [2] has an invalid digital signature. This may indicate that the cabinet file is corrupt.{{ Error [3] was returned by WinVerifyTrust.}} + Failed to correctly copy [2] file: CRC error. + Failed to correctly move [2] file: CRC error. + Failed to correctly patch [2] file: CRC error. + The file '[2]' cannot be installed because the file cannot be found in cabinet file '[3]'. This could indicate a network error, an error reading from the CD-ROM, or a problem with this package. + The cabinet file '[2]' required for this installation is corrupt and cannot be used. This could indicate a network error, an error reading from the CD-ROM, or a problem with this package. + There was an error creating a temporary file that is needed to complete this installation.{{ Folder: [3]. System error code: [2]}} + Could not create key: [2]. {{ System error [3].}} Verify that you have sufficient access to that key, or contact your support personnel. + Could not open key: [2]. {{ System error [3].}} Verify that you have sufficient access to that key, or contact your support personnel. + Could not delete value [2] from key [3]. {{ System error [4].}} Verify that you have sufficient access to that key, or contact your support personnel. + Could not delete key [2]. {{ System error [3].}} Verify that you have sufficient access to that key, or contact your support personnel. + Could not read value [2] from key [3]. {{ System error [4].}} Verify that you have sufficient access to that key, or contact your support personnel. + Could not write value [2] to key [3]. {{ System error [4].}} Verify that you have sufficient access to that key, or contact your support personnel. + Could not get value names for key [2]. {{ System error [3].}} Verify that you have sufficient access to that key, or contact your support personnel. + Could not get sub key names for key [2]. {{ System error [3].}} Verify that you have sufficient access to that key, or contact your support personnel. + Could not read security information for key [2]. {{ System error [3].}} Verify that you have sufficient access to that key, or contact your support personnel. + Could not increase the available registry space. [2] KB of free registry space is required for the installation of this application. + Another installation is in progress. You must complete that installation before continuing this one. + Error accessing secured data. Please make sure the Windows Installer is configured properly and try the install again. + User '[2]' has previously initiated an install for product '[3]'. That user will need to run that install again before they can use that product. Your current install will now continue. + User '[2]' has previously initiated an install for product '[3]'. That user will need to run that install again before they can use that product. + Out of disk space -- Volume: '[2]'; required space: [3] KB; available space: [4] KB. Free some disk space and retry. + Are you sure you want to cancel? + The file [2][3] is being held in use{ by the following process: Name: [4], Id: [5], Window Title: '[6]'}. Close that application and retry. + The product '[2]' is already installed, preventing the installation of this product. The two products are incompatible. + There is not enough disk space on the volume '[2]' to continue the install with recovery enabled. [3] KB are required, but only [4] KB are available. Click Ignore to continue the install without saving recovery information, click Retry to check for available space again, or click Cancel to quit the installation. + Could not access network location [2]. + The following applications should be closed before continuing the install: + Could not find any previously installed compliant products on the machine for installing this product. + An error occurred while applying security settings. [2] is not a valid user or group. This could be a problem with the package, or a problem connecting to a domain controller on the network. Check your network connection and click Retry, or Cancel to end the install. {{Unable to locate the user's SID, system error [3]}} + The key [2] is not valid. Verify that you entered the correct key. + The installer must restart your system before configuration of [2] can continue. Click Yes to restart now or No if you plan to manually restart later. + You must restart your system for the configuration changes made to [2] to take effect. Click Yes to restart now or No if you plan to manually restart later. + An installation for [2] is currently suspended. You must undo the changes made by that installation to continue. Do you want to undo those changes? + A previous installation for this product is in progress. You must undo the changes made by that installation to continue. Do you want to undo those changes? + An installation package for the product [2] cannot be found. Try the installation again using a valid copy of the installation package '[3]'. + Installation completed successfully. + Installation failed. + Product: [2] -- [3] + You may either restore your computer to its previous state or continue the install later. Would you like to restore? + An error occurred while writing installation information to disk. Check to make sure enough disk space is available, and click Retry, or Cancel to end the install. + One or more of the files required to restore your computer to its previous state could not be found. Restoration will not be possible. + [2] cannot install one of its required products. Contact your technical support group. {{System Error: [3].}} + The older version of [2] cannot be removed. Contact your technical support group. {{System Error [3].}} + Installed [2] + Configured [2] + Removed [2] + File [2] was rejected by digital signature policy. + The Windows Installer Service could not be accessed. This can occur if you are running Windows in safe mode, or if the Windows Installer is not correctly installed. Contact your support personnel for assistance. + There is a problem with this Windows Installer package. A script required for this install to complete could not be run. Contact your support personnel or package vendor. {{Custom action [2] script error [3], [4]: [5] Line [6], Column [7], [8] }} + There is a problem with this Windows Installer package. A program required for this install to complete could not be run. Contact your support personnel or package vendor. {{Action: [2], location: [3], command: [4] }} + There is a problem with this Windows Installer package. A program run as part of the setup did not finish as expected. Contact your support personnel or package vendor. {{Action [2], location: [3], command: [4] }} + There is a problem with this Windows Installer package. A DLL required for this install to complete could not be run. Contact your support personnel or package vendor. {{Action [2], entry: [3], library: [4] }} + Removal completed successfully. + Removal failed. + Advertisement completed successfully. + Advertisement failed. + Configuration completed successfully. + Configuration failed. + You must be an Administrator to remove this application. To remove this application, you can log on as an Administrator, or contact your technical support group for assistance. + The path [2] is not valid. Please specify a valid path. + Out of memory. Shut down other applications before retrying. + There is no disk in drive [2]. Please insert one and click Retry, or click Cancel to go back to the previously selected volume. + There is no disk in drive [2]. Please insert one and click Retry, or click Cancel to return to the browse dialog and select a different volume. + The folder [2] does not exist. Please enter a path to an existing folder. + You have insufficient privileges to read this folder. + A valid destination folder for the install could not be determined. + Error attempting to read from the source install database: [2]. + Scheduling reboot operation: Renaming file [2] to [3]. Must reboot to complete operation. + Scheduling reboot operation: Deleting file [2]. Must reboot to complete operation. + Module [2] failed to register. HRESULT [3]. Contact your support personnel. + Module [2] failed to unregister. HRESULT [3]. Contact your support personnel. + Failed to cache package [2]. Error: [3]. Contact your support personnel. + Could not register font [2]. Verify that you have sufficient permissions to install fonts, and that the system supports this font. + Could not unregister font [2]. Verify that you that you have sufficient permissions to remove fonts. + Could not create Shortcut [2]. Verify that the destination folder exists and that you can access it. + Could not remove Shortcut [2]. Verify that the shortcut file exists and that you can access it. + Could not register type library for file [2]. Contact your support personnel. + Could not unregister type library for file [2]. Contact your support personnel. + Could not update the ini file [2][3]. Verify that the file exists and that you can access it. + Could not schedule file [2] to replace file [3] on reboot. Verify that you have write permissions to file [3]. + Error removing ODBC driver manager, ODBC error [2]: [3]. Contact your support personnel. + Error installing ODBC driver manager, ODBC error [2]: [3]. Contact your support personnel. + Error removing ODBC driver: [4], ODBC error [2]: [3]. Verify that you have sufficient privileges to remove ODBC drivers. + Error installing ODBC driver: [4], ODBC error [2]: [3]. Verify that the file [4] exists and that you can access it. + Error configuring ODBC data source: [4], ODBC error [2]: [3]. Verify that the file [4] exists and that you can access it. + Service '[2]' ([3]) failed to start. Verify that you have sufficient privileges to start system services. + Service '[2]' ([3]) could not be stopped. Verify that you have sufficient privileges to stop system services. + Service '[2]' ([3]) could not be deleted. Verify that you have sufficient privileges to remove system services. + Service '[2]' ([3]) could not be installed. Verify that you have sufficient privileges to install system services. + Could not update environment variable '[2]'. Verify that you have sufficient privileges to modify environment variables. + You do not have sufficient privileges to complete this installation for all users of the machine. Log on as administrator and then retry this installation. + Could not set file security for file '[3]'. Error: [2]. Verify that you have sufficient privileges to modify the security permissions for this file. + Component Services (COM+ 1.0) are not installed on this computer. This installation requires Component Services in order to complete successfully. Component Services are available on Windows 2000. + Error registering COM+ Application. Contact your support personnel for more information. + Error unregistering COM+ Application. Contact your support personnel for more information. + The description for service '[2]' ([3]) could not be changed. + The Windows Installer service cannot update the system file [2] because the file is protected by Windows. You may need to update your operating system for this program to work correctly. {{Package version: [3], OS Protected version: [4]}} + The Windows Installer service cannot update the protected Windows file [2]. {{Package version: [3], OS Protected version: [4], SFP Error: [5]}} + The Windows Installer service cannot update one or more protected Windows files. {{SFP Error: [2]. List of protected files:\r\n[3]}} + User installations are disabled via policy on the machine. + An error occured during the installation of assembly component [2]. HRESULT: [3]. {{assembly interface: [4], function: [5], assembly name: [6]}} + + TARGETDIR="" + + + + + + + + + + + + + + + TARGETDIR="" + + + + + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + @@@@@]]> + + + + + + + + + + + TARGETDIR="" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + TARGETDIR="" + + + + + + + LIBUSBINSTALL=1 + + + \ No newline at end of file diff --git a/scripts/Windows/Makefile b/scripts/Windows/Makefile deleted file mode 100644 index abf1b97368..0000000000 --- a/scripts/Windows/Makefile +++ /dev/null @@ -1,2 +0,0 @@ -halt: halt.c - gcc -mwindows -mno-cygwin -s -o halt.exe halt.c diff --git a/scripts/Windows/Makefile.am b/scripts/Windows/Makefile.am new file mode 100644 index 0000000000..f2bfb8a8a4 --- /dev/null +++ b/scripts/Windows/Makefile.am @@ -0,0 +1,32 @@ +# Network UPS Tools: script/Windows + +AM_CFLAGS = -I$(top_srcdir)/include + +../include/nut_version.h: FORCE + (cd ../include/ && $(MAKE) $(AM_MAKEFLAGS) nut_version.h) + +EXTRA_DIST = winevent.mc build-mingw-nut.sh README + +FORCE: + +if HAVE_MINGW_RESGEN + +winevent.rc winevent.h: winevent.mc + $(WINDMC) $< + +winevent.o: winevent.rc winevent.h + $(WINDRES) winevent.rc winevent.o + +wininit.$(OBJEXT): winevent.h + +bin_PROGRAMS = nut halt + +nut_SOURCES = wininit.c +nut_LDADD = ../../common/libcommon.la winevent.o + +halt_SOURCES = halt.c + +CLEANFILES = winevent.rc winevent.o winevent.h + +endif HAVE_MINGW_RESGEN + diff --git a/scripts/Windows/README b/scripts/Windows/README new file mode 100644 index 0000000000..f21c65eb1f --- /dev/null +++ b/scripts/Windows/README @@ -0,0 +1,102 @@ +NUT and MS Windows +================== + +Introduction +------------ + +NUT is now also available for the Microsoft Windows platform. + +Cross compiling from Linux +-------------------------- + +Fortunately, you are not forced to have a real Windows system to compile NUT. +The following chapters will guide you through setting up up a +link:http://mingw-w64.sourceforge.net[MinGW-w64] build environment and +compiling NUT. + +MinGW-w64 build environment +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You will first need to setup a MinGW-w64 build environment. + +On Debian systems, use: + + # apt-get mingw-w64 + +On Redhat systems, use: + + # ??? + +You will also need pthread and mingw regex libraries. + +[NOTE] +================================================================================ + +When using the compilation approach, use the following HOST_FLAG, BUILD_FLAG +and CC, CFLAGS, LDFLAGS and PREFIX: + +- prefer to export ARCH="x86_64-w64-mingw32" or ARCH="i686-w64-mingw32" +- HOST_FLAG="--host=$ARCH" +- PREFIX=/usr/$ARCH + +- BUILD_FLAG="--build=`dpkg-architecture -qDEB_BUILD_GNU_TYPE`" +Note that this is very Debian specific! + +- also export the following compilation flags: + + export CFLAGS+="-D_POSIX=1 -I/usr/$ARCH/include/" + export LDFLAGS+="-L/usr/$ARCH/lib/" + +================================================================================ + + +pthread library +^^^^^^^^^^^^^^^ + +On Debian, you can use the following packages repository: + + link:https://launchpad.net/~mingw-packages/+archive/ppa[MinGW PPA] + +On Redhat: FIXME + +//////////////////////////////////////////////////////////////////////////////// +http://fedoraproject.org/wiki/MinGW/CrossCompilerFramework +https://fedoraproject.org/wiki/Packaging:MinGW?rd=Packaging:MinGW_Future +https://fedoraproject.org/wiki/Packaging:MinGW_Old +//////////////////////////////////////////////////////////////////////////////// + +You can also compile it using: + + $ wget http://mirrors.kernel.org/sources.redhat.com/pthreads-win32/pthreads-w32-2-8-0-release.tar.gz + $ make -f GNUmakefile "CROSS=i686-w64-mingw32-" $BUILD_FLAG GC-inlined + $ cp *.dll /usr/i686-w64-mingw32/pthreads/lib/ + $ cp *.a /usr/i686-w64-mingw32/lib/ + $ cp pthread.h sched.h semaphore.h /usr/i686-w64-mingw32/pthreads/include + + +MinGW regex library +^^^^^^^^^^^^^^^^^^^ + +You can compile it using: + + $ wget http://netcologne.dl.sourceforge.net/project/mingw/Other/UserContributed/regex/mingw-regex-2.5.1/mingw-libgnurx-2.5.1-src.tar.gz + $ tar -zxf mingw-libgnurx-2.5.1-src.tar.gz + $ cd mingw-libgnurx-2.5.1 + $ ./configure --prefix=/usr/i686-w64-mingw32/ --host=i686-w64-mingw32 + $ make + $ make install + +Other requirements +^^^^^^^^^^^^^^^^^^ + +libusb, Net SNMP, ... + +Building NUT +~~~~~~~~~~~~ + +Use the following to compile NUT: + + $ cd scripts/Windows/ + $ ./build-mingw-nut.sh + +If everything goes fine, you will find a NUT installation tree in 'nut_install'. diff --git a/scripts/Windows/build-mingw-nut.sh b/scripts/Windows/build-mingw-nut.sh new file mode 100755 index 0000000000..83a096856a --- /dev/null +++ b/scripts/Windows/build-mingw-nut.sh @@ -0,0 +1,66 @@ +#!/bin/bash +# script to cross compile NUT for Windows from Linux using MinGW-w64 +# http://mingw-w64.sourceforge.net/ + +#set -x + +# default to update source then build +WINDIR=$(pwd) +TOP_DIR=$WINDIR/../.. +BUILD_DIR=$WINDIR/nut_build +INSTALL_DIR=$WINDIR/nut_install + +VER_OPT=2.6.5 +DEBUG=true + +# default to 32bits build +cmd=all32 +if [ -n "$1" ] ; then + cmd=$1 +fi + +# FIXME +# Stable version (download the latest stable archive) +# if [ ! -f nut-$VER_OPT.tar.gz ] ; then +# wget http://www.networkupstools.org/source/2.6/nut-$VER_OPT.tar.gz +# fi +# rm -rf nut-$VER_OPT +# tar -xzf nut-$VER_OPT.tar.gz +# mv nut-$VER_OPT $BUILD_DIR + +# In-place version (no download) +rm -rf $BUILD_DIR $INSTALL_DIR +cd ../.. +rm -f nut-?.?.?.tar.gz +make dist +SRC_ARCHIVE=$(ls nut-?.?.?.tar.gz) +cd scripts/Windows +tar -xzf ../../$SRC_ARCHIVE +mv nut-?.?.? $BUILD_DIR +cd $BUILD_DIR + +if [ "$cmd" == "all64" ] || [ "$cmd" == "b64" ] || [ "$cmd" == "all32" ] || [ "$cmd" == "b32" ] ; then + ARCH="x86_64-w64-mingw32" + if [ "$cmd" == "all32" ] || [ "$cmd" == "b32" ] ; then + ARCH="i686-w64-mingw32" + fi + + HOST_FLAG="--host=$ARCH" + # --build needs to be specified, beside of --host, to avoid Warning + # but this version is very Debian specific!!! + # FIXME: find something more generic + BUILD_FLAG="--build=`dpkg-architecture -qDEB_BUILD_GNU_TYPE`" + export CC="$ARCH-gcc" + export PATH=/usr/$ARCH/bin:$PATH + + export CFLAGS+="-D_POSIX=1 -I/usr/$ARCH/include/" + export LDFLAGS+="-L/usr/$ARCH/lib/" + ./configure $HOST_FLAG $BUILD_FLAG --prefix=$INSTALL_DIR + make 1>/dev/null + make install + cd .. +else + echo "Usage:" + echo " $0 [all64 | b64 | all32 | b32]" + echo " Default: 'all32'" +fi diff --git a/scripts/Windows/winevent.mc b/scripts/Windows/winevent.mc new file mode 100644 index 0000000000..2bd2545ca0 --- /dev/null +++ b/scripts/Windows/winevent.mc @@ -0,0 +1,28 @@ +MessageIdTypedef=DWORD + +SeverityNames=(Success=0x0:STATUS_SEVERITY_SUCCESS + Informational=0x1:STATUS_SEVERITY_INFORMATIONAL + Warning=0x2:STATUS_SEVERITY_WARNING + Error=0x3:STATUS_SEVERITY_ERROR + ) + + +FacilityNames=(System=0x0:FACILITY_SYSTEM + Runtime=0x2:FACILITY_RUNTIME + Stubs=0x3:FACILITY_STUBS + Io=0x4:FACILITY_IO_ERROR_CODE + ) + +LanguageNames=(English=0x409:MSG00409) + +; // The following are message definitions. + +MessageId=0x1 +Severity=Error +Facility=Runtime +SymbolicName=SVC_EVENT +Language=English +%1. +. + + diff --git a/scripts/Windows/wininit.c b/scripts/Windows/wininit.c new file mode 100644 index 0000000000..4395162271 --- /dev/null +++ b/scripts/Windows/wininit.c @@ -0,0 +1,640 @@ +/* wininit.c - MS Windows service which replace the init script + + Copyright (C) + 2010 Frederic Bohe + + 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 +*/ +#ifdef WIN32 + +#include "common.h" +#include "config.h" +#include "winevent.h" +#include "wincompat.h" + +#define NUT_START TRUE +#define NUT_STOP FALSE + +typedef struct conn_s { + HANDLE handle; + OVERLAPPED overlapped; + char buf[LARGEBUF]; + struct conn_s *prev; + struct conn_s *next; +} conn_t; + +static DWORD upsd_pid = 0; +static DWORD upsmon_pid = 0; +static BOOL service_flag = TRUE; +HANDLE svc_stop = NULL; +static SERVICE_STATUS SvcStatus; +static SERVICE_STATUS_HANDLE SvcStatusHandle; + +static void print_event(DWORD priority, const char * fmt, ...) +{ + HANDLE EventSource; + va_list ap; + CHAR * buf; + int ret; + + buf = xmalloc(LARGEBUF); + + va_start(ap, fmt); + ret = vsnprintf(buf, LARGEBUF, fmt, ap); + va_end(ap); + + if(ret<0) { + return; + } + + if( !service_flag ) { + upslogx(LOG_ERR, "EventLog : %s\n",buf); + } + + EventSource = RegisterEventSource(NULL, SVCNAME); + + if( NULL != EventSource ) { + ReportEvent( EventSource, /* event log handle */ + priority, /* event type */ + 0, /* event category */ + SVC_EVENT, /* event identifier */ + NULL, /* no security identifier*/ + 1, /* size of string array */ + 0, /* no binary data */ + (const char **)&buf, /* array of string */ + NULL); /* no binary data */ + + DeregisterEventSource(EventSource); + + } + + if( buf ) + free(buf); +} + +/* returns PID of the newly created process or 0 on failure */ +static DWORD create_process(char * command) +{ + STARTUPINFO StartupInfo; + PROCESS_INFORMATION ProcessInformation; + BOOL res; + DWORD LastError; + + memset(&StartupInfo,0,sizeof(STARTUPINFO)); + StartupInfo.cb = sizeof(StartupInfo); + memset(&ProcessInformation,0,sizeof(ProcessInformation)); + + res = CreateProcess( + NULL, + command, + NULL, + NULL, + FALSE, + CREATE_NEW_PROCESS_GROUP, + NULL, + NULL, + &StartupInfo, + &ProcessInformation + ); + LastError = GetLastError(); + + if( res == 0 ) { + print_event(LOG_ERR, "Can't create process %s : %d", command, LastError); + return 0; + } + + return ProcessInformation.dwProcessId; +} + +/* return PID of created process or 0 on failure */ +static DWORD run_drivers() +{ + char command[MAX_PATH]; + char *path; + + path = getfullpath(PATH_BIN); + snprintf(command,sizeof(command),"%s\\upsdrvctl.exe start",path); + free(path); + return create_process(command); +} + +/* return PID of created process or 0 on failure */ +static DWORD stop_drivers() +{ + char command[MAX_PATH]; + char *path; + + path = getfullpath(PATH_BIN); + snprintf(command,sizeof(command),"%s\\upsdrvctl.exe stop",path); + free(path); + return create_process(command); +} + +/* return PID of created process or 0 on failure */ +static void run_upsd() +{ + char command[MAX_PATH]; + char *path; + + path = getfullpath(PATH_SBIN); + snprintf(command,sizeof(command),"%s\\upsd.exe",path); + free(path); + upsd_pid = create_process(command); +} + +static void stop_upsd() +{ + if ( sendsignal( UPSD_PIPE_NAME, COMMAND_STOP ) ) { + print_event(LOG_ERR, "Error stopping upsd (%d)",GetLastError()); + } +} + +/* return PID of created process or 0 on failure */ +static void run_upsmon() +{ + char command[MAX_PATH]; + char *path; + + path = getfullpath(PATH_SBIN); + snprintf(command,sizeof(command),"%s\\upsmon.exe",path); + free(path); + upsmon_pid = create_process(command); +} + +static void stop_upsmon() +{ + if ( sendsignal( UPSMON_PIPE_NAME, COMMAND_STOP ) ) { + print_event(LOG_ERR, "Error stopping upsmon (%d)",GetLastError()); + } +} + +/* Return 0 if powerdown flag is set */ +static DWORD test_powerdownflag() +{ + char command[MAX_PATH]; + char *path; + STARTUPINFO StartupInfo; + PROCESS_INFORMATION ProcessInformation; + BOOL res; + DWORD LastError; + DWORD status; + int i = 10; + int timeout = 500; + + path = getfullpath(PATH_SBIN); + snprintf(command,sizeof(command),"%s\\upsmon.exe -K",path); + free(path); + + memset(&StartupInfo,0,sizeof(STARTUPINFO)); + StartupInfo.cb = sizeof(StartupInfo); + memset(&ProcessInformation,0,sizeof(ProcessInformation)); + + res = CreateProcess( + NULL, + command, + NULL, + NULL, + FALSE, + CREATE_NEW_PROCESS_GROUP, + NULL, + NULL, + &StartupInfo, + &ProcessInformation + ); + LastError = GetLastError(); + + if( res == 0 ) { + print_event(LOG_ERR, "Can't create process %s : %d", command, LastError); + return 1; + } + + while( i > 0) { + res = GetExitCodeProcess(ProcessInformation.hProcess, &status); + if( res != 0) { + if( status != STILL_ACTIVE) { + return status; + } + } + Sleep(timeout); + i--; + } + + return 1; +} + +static DWORD shutdown_ups() +{ + char command[MAX_PATH]; + char *path; + + path = getfullpath(PATH_BIN); + snprintf(command,sizeof(command),"%s\\upsdrvctl.exe shutdown",path); + free(path); + return create_process(command); +} + +/* return 0 on failure */ +static int parse_nutconf(BOOL start_flag) +{ + char fn[SMALLBUF]; + FILE *nutf; + char buf[SMALLBUF]; + char fullname[SMALLBUF]; + + snprintf(fn,sizeof(fn),"%s/nut.conf",confpath()); + + nutf = fopen(fn, "r"); + if(nutf == NULL) { + snprintf(buf,sizeof(buf),"Error opening %s",fn); + print_event(LOG_ERR,buf); + return 0; + } + + while( fgets(buf,sizeof(buf),nutf) != NULL ) { + if(buf[0] != '#') { + if( strstr(buf,"standalone") != NULL || + strstr(buf,"netserver") != NULL ) { + if( start_flag == NUT_START ) { + print_event(LOG_INFO,"Starting drivers"); + run_drivers(); + print_event(LOG_INFO,"Starting upsd"); + run_upsd(); + /* Wait a moment for the drivers to start */ + Sleep(5000); + print_event(LOG_INFO,"Starting upsmon"); + run_upsmon(); + return 1; + } + else { + print_event(LOG_INFO,"stop upsd"); + stop_upsd(); + print_event(LOG_INFO,"stop drivers"); + stop_drivers(); + print_event(LOG_INFO,"stop upsmon"); + stop_upsmon(); + /* Give a chance to upsmon to write the POWERDOWNFLAG file */ + Sleep(1000); + if( test_powerdownflag() == 0 ) { + print_event(LOG_INFO,"shutdown ups"); + shutdown_ups(); + } + print_event(LOG_INFO,"End of NUT stop"); + return 1; + } + } + if( strstr(buf,"netclient") != NULL ) { + if( start_flag == NUT_START ) { + run_upsmon(); + return 1; + } + else { + stop_upsmon(); + return 1; + } + } + } + } + + GetFullPathName(fn,sizeof(fullname),fullname,NULL); + snprintf(buf,sizeof(buf),"nut disabled, please adjust the configuration to your needs. Then set MODE to a suitable value in %s to enable it.",fullname); + print_event(LOG_ERR,buf); + return 0; +} + +static int SvcInstall(const char * SvcName, const char * args) +{ + SC_HANDLE SCManager; + SC_HANDLE Service; + TCHAR Path[MAX_PATH]; + + if( !GetModuleFileName( NULL, Path, MAX_PATH ) ) { + printf("Cannot install service (%d)\n", (int)GetLastError()); + return EXIT_FAILURE; + } + + if( args != NULL ) { + snprintfcat(Path, sizeof(Path), " %s", args); + } + + SCManager = OpenSCManager( + NULL, /* local computer */ + NULL, /* ServiceActive database */ + SC_MANAGER_ALL_ACCESS); /* full access rights */ + + if (NULL == SCManager) { + upslogx(LOG_ERR, "OpenSCManager failed (%d)\n", (int)GetLastError()); + return EXIT_FAILURE; + } + + Service = CreateService( + SCManager, /* SCM database */ + SvcName, /* name of service */ + SvcName, /* service name to display */ + SERVICE_ALL_ACCESS, /* desired access */ + SERVICE_WIN32_OWN_PROCESS, /* service type */ + SERVICE_AUTO_START, /* start type */ + SERVICE_ERROR_NORMAL, /* error control type */ + Path, /* path to service binary */ + NULL, /* no load ordering group */ + NULL, /* no tag identifier */ + NULL, /* no dependencies */ + NULL, /* LocalSystem account */ + NULL); /* no password */ + + if (Service == NULL) { + upslogx(LOG_ERR, "CreateService failed (%d)\n", (int)GetLastError()); + CloseServiceHandle(SCManager); + return EXIT_FAILURE; + } + else { + upslogx(LOG_INFO, "Service installed successfully\n"); + } + + CloseServiceHandle(Service); + CloseServiceHandle(SCManager); + + return EXIT_SUCCESS; +} + +static int SvcUninstall(const char * SvcName) +{ + SC_HANDLE SCManager; + SC_HANDLE Service; + + SCManager = OpenSCManager( + NULL, /* local computer */ + NULL, /* ServicesActive database */ + SC_MANAGER_ALL_ACCESS); /* full access rights */ + + if (NULL == SCManager) { + upslogx(LOG_ERR, "OpenSCManager failed (%d)\n", (int)GetLastError()); + return EXIT_FAILURE; + } + + Service = OpenService( + SCManager, /* SCM database */ + SvcName, /* name of service */ + DELETE); /* need delete access */ + + if (Service == NULL) { + upslogx(LOG_ERR, "OpenService failed (%d)\n", (int)GetLastError()); + CloseServiceHandle(SCManager); + return EXIT_FAILURE; + } + + if (! DeleteService(Service) ) { + upslogx(LOG_ERR,"DeleteService failed (%d)\n", (int)GetLastError()); + } + else { + upslogx(LOG_ERR,"Service deleted successfully\n"); + } + + CloseServiceHandle(Service); + CloseServiceHandle(SCManager); + + return EXIT_SUCCESS; +} + +static void ReportSvcStatus( DWORD CurrentState, + DWORD Win32ExitCode, + DWORD WaitHint) +{ + static DWORD CheckPoint = 1; + + SvcStatus.dwCurrentState = CurrentState; + SvcStatus.dwWin32ExitCode = Win32ExitCode; + SvcStatus.dwWaitHint = WaitHint; + + if (CurrentState == SERVICE_START_PENDING) + SvcStatus.dwControlsAccepted = 0; + else SvcStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN; + + if ( (CurrentState == SERVICE_RUNNING) || + (CurrentState == SERVICE_STOPPED) ) { + SvcStatus.dwCheckPoint = 0; + } + else { + SvcStatus.dwCheckPoint = CheckPoint++; + } + + /* report the status of the service to the SCM */ + SetServiceStatus( SvcStatusHandle, &SvcStatus ); +} + +static void WINAPI SvcCtrlHandler( DWORD Ctrl ) +{ + switch(Ctrl) + { + case SERVICE_CONTROL_STOP: + case SERVICE_CONTROL_SHUTDOWN: + ReportSvcStatus(SERVICE_STOP_PENDING, NO_ERROR, 0); + + /* Signal the service to stop */ + SetEvent(svc_stop); + ReportSvcStatus(SvcStatus.dwCurrentState, NO_ERROR, 0); + + return; + + case SERVICE_CONTROL_INTERROGATE: + break; + + default: + break; + } +} + +static void SvcStart(char * SvcName) +{ + /* Register the handler function for the service */ + SvcStatusHandle = RegisterServiceCtrlHandler( + SvcName, + SvcCtrlHandler); + + if( !SvcStatusHandle ) { + upslogx(LOG_ERR, "RegisterServiceCtrlHandler\n"); + return; + } + + SvcStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; + SvcStatus.dwServiceSpecificExitCode = 0; + + /* Report initial status to the SCM */ + ReportSvcStatus( SERVICE_START_PENDING, NO_ERROR, 3000 ); +} + +static void SvcReady(void) +{ + svc_stop = CreateEvent( + NULL, /* default security attributes */ + TRUE, /* manual reset event */ + FALSE, /* not signaled */ + NULL); /* no name */ + + if( svc_stop == NULL ) { + ReportSvcStatus( SERVICE_STOPPED, NO_ERROR, 0); + return; + } + ReportSvcStatus( SERVICE_RUNNING, NO_ERROR, 0); +} + +static void close_all(void) +{ + pipe_conn_t *conn; + + for (conn = pipe_connhead; conn; conn = conn->next) { + pipe_disconnect(conn); + } + +} + +static void WINAPI SvcMain( DWORD argc, LPTSTR *argv ) +{ + + DWORD ret; + HANDLE handles[MAXIMUM_WAIT_OBJECTS]; + int maxhandle = 0; + pipe_conn_t *conn; + DWORD priority; + char * buf; + + if(service_flag) { + SvcStart(SVCNAME); + } + + /* A service has no console, so do has its children. */ + /* So if we want to be able to send CTRL+BREAK signal we must */ + /* create a console which will be inherited by children */ + AllocConsole(); + + print_event(LOG_INFO,"Starting"); + + /* pipe for event log proxy */ + pipe_create(EVENTLOG_PIPE_NAME); + + /* parse nut.conf and start relevant processes */ + if ( parse_nutconf(NUT_START) == 0 ) { + print_event(LOG_INFO, "exiting"); + if( service_flag ) { + ReportSvcStatus( SERVICE_STOPPED, NO_ERROR, 0); + } + return; + } + + if(service_flag) { + SvcReady(); + } + + while (1) { + maxhandle = 0; + memset(&handles,0,sizeof(handles)); + + /* Wait on the read IO of each connections */ + for (conn = pipe_connhead; conn; conn = conn->next) { + handles[maxhandle] = conn->overlapped.hEvent; + maxhandle++; + } + /* Add the new pipe connected event */ + handles[maxhandle] = pipe_connection_overlapped.hEvent; + maxhandle++; + + /* Add SCM event handler in service mode*/ + if(service_flag) { + handles[maxhandle] = svc_stop; + maxhandle++; + } + + ret = WaitForMultipleObjects(maxhandle,handles,FALSE,INFINITE); + + if (ret == WAIT_FAILED) { + print_event(LOG_ERR, "Wait failed"); + return; + } + + if( handles[ret] == svc_stop && service_flag ) { + parse_nutconf(NUT_STOP); + if(service_flag) { + print_event(LOG_INFO, "Exiting"); + close_all(); + ReportSvcStatus( SERVICE_STOPPED, NO_ERROR, 0); + } + return; + } + + /* Retrieve the signaled connection */ + for(conn = pipe_connhead; conn != NULL; conn = conn->next) { + if( conn->overlapped.hEvent == handles[ret-WAIT_OBJECT_0]) { + break; + } + } + /* a new pipe connection has been signaled */ + if (handles[ret] == pipe_connection_overlapped.hEvent) { + pipe_connect(); + } + /* one of the read event handle has been signaled */ + else { + if( conn != NULL) { + if( pipe_ready(conn) ) { + buf = conn->buf; + /* a frame is a DWORD indicating priority followed by an array of char (not necessarily followed by a terminal 0 */ + priority =*((DWORD *)buf); + buf = buf + sizeof(DWORD); + print_event(priority,buf); + + pipe_disconnect(conn); + } + } + } + } +} + +int main(int argc, char **argv) +{ + int i; + while ((i = getopt(argc, argv, "+IUN")) != -1) { + switch (i) { + case 'I': + return SvcInstall(SVCNAME,NULL); + case 'U': + return SvcUninstall(SVCNAME); + case 'N': + service_flag = FALSE; + upslogx(LOG_ERR, "Running in non-service mode\n"); + break; + default: + break; + } + } + + optind = 0; + + SERVICE_TABLE_ENTRY DispatchTable[] = + { + { SVCNAME, (LPSERVICE_MAIN_FUNCTION) SvcMain }, + { NULL, NULL } + }; + /* This call returns when the service has stopped */ + if(service_flag ) { + if (!StartServiceCtrlDispatcher( DispatchTable )) + { + print_event(LOG_ERR, "StartServiceCtrlDispatcher failed : exiting, this is a Windows service which can't be run as a regular application by default. Try -N to start it as a regular application"); + } + } + else { + SvcMain(argc,argv); + } + + return EXIT_SUCCESS; +} +#endif /* WIN32 */ diff --git a/server/.gitignore b/server/.gitignore index 517aa6c6d2..04bf3ef009 100644 --- a/server/.gitignore +++ b/server/.gitignore @@ -3,3 +3,4 @@ upsd .deps Makefile.in .libs +*.exe diff --git a/server/Makefile.am b/server/Makefile.am index 8146568a09..664a07d30d 100644 --- a/server/Makefile.am +++ b/server/Makefile.am @@ -27,4 +27,8 @@ upsd_SOURCES = upsd.c user.c conf.c netssl.c sstate.c desc.c \ netlist.h netmisc.h netset.h netuser.h netssl.h sstate.h stype.h upsd.h \ upstype.h user-data.h user.h -sockdebug_SOURCES = sockdebug.c +if HAVE_WINDOWS +sockdebug_SOURCES = pipedebug.c +else !HAVE_WINDOWS + sockdebug_SOURCES = sockdebug.c +endif !HAVE_WINDOWS diff --git a/server/conf.c b/server/conf.c index aea8220cd7..1e439b7d17 100644 --- a/server/conf.c +++ b/server/conf.c @@ -50,6 +50,19 @@ static void ups_create(const char *fn, const char *name, const char *desc) temp->stale = 1; temp->retain = 1; +#ifdef WIN32 + memset(&temp->read_overlapped,0,sizeof(temp->read_overlapped)); + memset(temp->buf,0,sizeof(temp->buf)); + temp->read_overlapped.hEvent = CreateEvent(NULL, /* Security */ + FALSE, /* auto-reset*/ + FALSE, /* initial state = non signaled */ + NULL /* no name */); + if(temp->read_overlapped.hEvent == NULL ) { + upslogx(LOG_ERR, "Can't create event for UPS [%s]", + name); + return; + } +#endif temp->sock_fd = sstate_connect(temp); /* preload this to the current time to avoid false staleness */ @@ -90,8 +103,13 @@ static void ups_update(const char *fn, const char *name, const char *desc) sstate_cmdfree(temp); pconf_finish(&temp->sock_ctx); +#ifndef WIN32 close(temp->sock_fd); temp->sock_fd = -1; +#else + CloseHandle(temp->sock_fd); + temp->sock_fd = INVALID_HANDLE_VALUE; +#endif temp->dumpdone = 0; /* now redefine the filename and wrap up */ @@ -382,8 +400,13 @@ static void delete_ups(upstype_t *target) else last->next = ptr->next; +#ifndef WIN32 if (ptr->sock_fd != -1) close(ptr->sock_fd); +#else + if (ptr->sock_fd != INVALID_HANDLE_VALUE) + CloseHandle(ptr->sock_fd); +#endif /* release memory */ sstate_infofree(ptr); diff --git a/server/netssl.c b/server/netssl.c index 11d511a4d9..7719bf2219 100644 --- a/server/netssl.c +++ b/server/netssl.c @@ -24,8 +24,13 @@ */ #include +#ifndef WIN32 #include #include +#else +#include +#endif + #include "upsd.h" #include "neterr.h" diff --git a/server/nut_ctype.h b/server/nut_ctype.h index c2302aadb8..545cca978f 100644 --- a/server/nut_ctype.h +++ b/server/nut_ctype.h @@ -60,6 +60,9 @@ typedef struct nut_ctype_s { /* doubly linked list */ struct nut_ctype_s *prev; struct nut_ctype_s *next; +#ifdef WIN32 + HANDLE Event; +#endif } nut_ctype_t; #endif /* NUT_CTYPE_H_SEEN */ diff --git a/server/pipedebug.c b/server/pipedebug.c new file mode 100644 index 0000000000..940bf5061a --- /dev/null +++ b/server/pipedebug.c @@ -0,0 +1,175 @@ +/* pipe.c - Network UPS Tools driver-server pipe debugger + + Copyright (C) 2012 Frederic Bohe + + 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 +*/ + + +#include "common.h" +#include "parseconf.h" + + +PCONF_CTX_t pipe_ctx; + +static void pipe_arg(int numarg, char **arg) +{ + int i; + + printf("numarg=%d : ", numarg); + + for (i = 0; i < numarg; i++) + printf("[%s] ", arg[i]); + + printf("\n"); +} + +static HANDLE pipe_connect(const char *pipefn) +{ + HANDLE fd; + char pipename[SMALLBUF]; + BOOL result = FALSE; + + snprintf(pipename, sizeof(pipename), "\\\\.\\pipe\\%s", pipefn); + + result = WaitNamedPipe(pipename,NMPWAIT_USE_DEFAULT_WAIT); + + if( result == FALSE ) { + printf("WaitNamedPipe : %d\n",GetLastError()); + exit(EXIT_FAILURE); + } + + fd = CreateFile( + pipename, // pipe name + GENERIC_READ | // read and write access + GENERIC_WRITE, + 0, // no sharing + NULL, // default security attributes FIXME + OPEN_EXISTING, // opens existing pipe + FILE_FLAG_OVERLAPPED, // enable async IO + NULL); // no template file + + if (fd == INVALID_HANDLE_VALUE) { + printf("CreateFile : %d\n",GetLastError()); + exit(EXIT_FAILURE); + } + + return fd; +} + +static void read_buf(char * buf, DWORD num) +{ + unsigned int i; + + for (i = 0; i < num; i++) { + + switch (pconf_char(&pipe_ctx, buf[i])) { + case 1: + pipe_arg(pipe_ctx.numargs, pipe_ctx.arglist); + break; + + case -1: + printf("Parse error: [%s]\n", pipe_ctx.errmsg); + break; + } + } +} + +DWORD WINAPI ReadThread( LPVOID lpParameter ) +{ + HANDLE pipefd = *((HANDLE *)lpParameter); + DWORD bytes_read; + char pipe_buf[SMALLBUF]; + OVERLAPPED pipe_overlapped; + + pconf_init(&pipe_ctx, NULL); + + memset(&pipe_overlapped,0,sizeof(pipe_overlapped)); + pipe_overlapped.hEvent = CreateEvent(NULL,FALSE,FALSE,NULL); + + for (;;) { + memset(pipe_buf,0,sizeof(pipe_buf)); + ReadFile(pipefd,pipe_buf,sizeof(pipe_buf),NULL,&pipe_overlapped); + GetOverlappedResult(pipefd,&pipe_overlapped,&bytes_read,TRUE); + read_buf(pipe_buf,bytes_read); + } +} + +DWORD WINAPI WriteThread( LPVOID lpParameter ) +{ + HANDLE pipefd = *((HANDLE *)lpParameter); + HANDLE hStdin; + DWORD bytes_read; + char stdin_buf[SMALLBUF]; + OVERLAPPED pipe_overlapped; + + hStdin = GetStdHandle(STD_INPUT_HANDLE); + + memset(&pipe_overlapped,0,sizeof(pipe_overlapped)); + pipe_overlapped.hEvent = CreateEvent(NULL,FALSE,FALSE,NULL); + + for (;;) { + ReadFile(hStdin,stdin_buf,sizeof(stdin_buf),&bytes_read,NULL); + WriteFile(pipefd,stdin_buf,bytes_read,NULL,&pipe_overlapped); + } +} +int main(int argc, char **argv) +{ + const char *prog = xbasename(argv[0]); + HANDLE pipefd; + HANDLE thread[2]; + + if (argc != 2) { + fprintf(stderr, "usage: %s \n", prog); + fprintf(stderr, " %s apcsmart-com1\n", + argv[0]); + exit(EXIT_SUCCESS); + } + + pipefd = pipe_connect(argv[1]); + + printf("connected: fd %d\n", pipefd); + + thread[0] = CreateThread( + NULL, /* security */ + 0, /* stack size */ + ReadThread, /* func */ + &pipefd,/* func param */ + 0, /* flags */ + NULL ); /* thread id */ + + if(thread[0] == NULL) { + fprintf(stderr, "CreateThread ReadThread failed\n"); + exit(EXIT_FAILURE); + } + + thread[1] = CreateThread( + NULL, /* security */ + 0, /* stack size */ + WriteThread, /* func */ + &pipefd,/* func param */ + 0, /* flags */ + NULL ); /* thread id */ + + if(thread[1] == NULL) { + fprintf(stderr, "CreateThread WriteThread failed\n"); + exit(EXIT_FAILURE); + } + + WaitForMultipleObjects(2,thread,TRUE,INFINITE); + + /* NOTREACHED */ + exit(EXIT_FAILURE); +} diff --git a/server/sstate.c b/server/sstate.c index 15d2c1412e..b1dc119de1 100644 --- a/server/sstate.c +++ b/server/sstate.c @@ -32,8 +32,10 @@ #include #include #include +#ifndef WIN32 #include #include +#endif static int parse_args(upstype_t *ups, int numargs, char **arg) { @@ -137,13 +139,32 @@ static void sendping(upstype_t *ups) int ret; const char *cmd = "PING\n"; +#ifndef WIN32 if ((!ups) || (ups->sock_fd < 0)) { +#else + if ((!ups) || (ups->sock_fd == INVALID_HANDLE_VALUE)) { +#endif return; } upsdebugx(3, "Pinging UPS [%s]", ups->name); +#ifndef WIN32 ret = write(ups->sock_fd, cmd, strlen(cmd)); +#else + DWORD bytesWritten = 0; + BOOL result = FALSE; + + result = WriteFile (ups->sock_fd,cmd,strlen(cmd),&bytesWritten,NULL); + if( result == 0 ) { + /* Write failed */ + ret = 0; + } + else { + ret = (int)bytesWritten; + } +#endif + if (ret != (int)strlen(cmd)) { upslog_with_errno(LOG_NOTICE, "Send ping to UPS [%s] failed", ups->name); @@ -156,6 +177,7 @@ static void sendping(upstype_t *ups) /* interface */ +#ifndef WIN32 int sstate_connect(upstype_t *ups) { int ret, fd; @@ -217,6 +239,51 @@ int sstate_connect(upstype_t *ups) return -1; } +#else +HANDLE sstate_connect(upstype_t *ups) +{ + HANDLE fd; + char pipename[SMALLBUF]; + const char *dumpcmd = "DUMPALL\n"; + BOOL result = FALSE; + + snprintf(pipename, sizeof(pipename), "\\\\.\\pipe\\%s", ups->fn); + + result = WaitNamedPipe(pipename,NMPWAIT_USE_DEFAULT_WAIT); + + if( result == FALSE ) { + return INVALID_HANDLE_VALUE; + } + + fd = CreateFile( + pipename, // pipe name + GENERIC_READ | // read and write access + GENERIC_WRITE, + 0, // no sharing + NULL, // default security attributes FIXME + OPEN_EXISTING, // opens existing pipe + FILE_FLAG_OVERLAPPED, // enable async IO + NULL); // no template file + + if (fd == INVALID_HANDLE_VALUE) { + upslog_with_errno(LOG_ERR, "Can't connect to UPS [%s] (%s)", ups->name, ups->fn); + return INVALID_HANDLE_VALUE; + } + + /* get a dump started so we have a fresh set of data */ + DWORD bytesWritten = 0; + + result = WriteFile (fd,dumpcmd,strlen(dumpcmd),&bytesWritten,NULL); + if (result == 0 || bytesWritten != strlen(dumpcmd)) { + upslog_with_errno(LOG_ERR, "Initial write to UPS [%s] failed", ups->name); + CloseHandle(fd); + return INVALID_HANDLE_VALUE; + } + + /* Start a read IO so we could wait on the event associated with it */ + ReadFile(fd,ups->buf,sizeof(ups->buf)-1,NULL,&(ups->read_overlapped)); /*-1 to be sure to have a trailling 0 */ +#endif + pconf_init(&ups->sock_ctx, NULL); ups->dumpdone = 0; @@ -235,7 +302,11 @@ int sstate_connect(upstype_t *ups) void sstate_disconnect(upstype_t *ups) { +#ifndef WIN32 if ((!ups) || (ups->sock_fd < 0)) { +#else + if ((!ups) || (ups->sock_fd == INVALID_HANDLE_VALUE)) { +#endif return; } @@ -244,13 +315,20 @@ void sstate_disconnect(upstype_t *ups) pconf_finish(&ups->sock_ctx); +#ifndef WIN32 close(ups->sock_fd); ups->sock_fd = -1; +#else + CloseHandle(ups->sock_fd); + ups->sock_fd = INVALID_HANDLE_VALUE; +#endif } void sstate_readline(upstype_t *ups) { int i, ret; + +#ifndef WIN32 char buf[SMALLBUF]; if ((!ups) || (ups->sock_fd < 0)) { @@ -272,6 +350,18 @@ void sstate_readline(upstype_t *ups) return; } } +#else + + if ((!ups) || (ups->sock_fd == INVALID_HANDLE_VALUE)) { + return; + } + char *buf = ups->buf; + DWORD bytesRead; + GetOverlappedResult(ups->sock_fd, &ups->read_overlapped, &bytesRead, FALSE); + ret = bytesRead; +#endif + + for (i = 0; i < ret; i++) { @@ -293,6 +383,12 @@ void sstate_readline(upstype_t *ups) return; } } + +#ifdef WIN32 + /* Restart async read */ + memset(ups->buf,0,sizeof(ups->buf)); + ReadFile( ups->sock_fd, ups->buf, sizeof(ups->buf)-1,NULL, &(ups->read_overlapped)); /* -1 to be sure to have a trailing 0 */ +#endif } const char *sstate_getinfo(const upstype_t *ups, const char *var) @@ -331,7 +427,11 @@ int sstate_dead(upstype_t *ups, int maxage) double elapsed; /* an unconnected ups is always dead */ +#ifndef WIN32 if (ups->sock_fd < 0) { +#else + if (ups->sock_fd == INVALID_HANDLE_VALUE) { +#endif upsdebugx(3, "sstate_dead: connection to driver socket for UPS [%s] lost", ups->name); return 1; /* dead */ } @@ -378,11 +478,29 @@ int sstate_sendline(upstype_t *ups, const char *buf) { int ret; +#ifndef WIN32 if ((!ups) ||(ups->sock_fd < 0)) { +#else + if ((!ups) ||(ups->sock_fd == INVALID_HANDLE_VALUE)) { +#endif return 0; /* failed */ } +#ifndef WIN32 ret = write(ups->sock_fd, buf, strlen(buf)); +#else + DWORD bytesWritten = 0; + BOOL result = FALSE; + + result = WriteFile (ups->sock_fd,buf,strlen(buf),&bytesWritten,NULL); + + if( result == 0 ) { + ret = 0; + } + else { + ret = (int)bytesWritten; + } +#endif if (ret == (int)strlen(buf)) { return 1; diff --git a/server/sstate.h b/server/sstate.h index fe8485ced3..7073000961 100644 --- a/server/sstate.h +++ b/server/sstate.h @@ -35,7 +35,11 @@ extern "C" { /* *INDENT-ON* */ #endif +#ifndef WIN32 int sstate_connect(upstype_t *ups); +#else +HANDLE sstate_connect(upstype_t *ups); +#endif void sstate_disconnect(upstype_t *ups); void sstate_readline(upstype_t *ups); const char *sstate_getinfo(const upstype_t *ups, const char *var); diff --git a/server/stype.h b/server/stype.h index c143018743..a9f5a9eb0d 100644 --- a/server/stype.h +++ b/server/stype.h @@ -21,7 +21,9 @@ #ifndef STYPE_H_SEEN #define STYPE_H_SEEN 1 +#ifndef WIN32 #include +#endif #ifndef NI_MAXHOST #define NI_MAXHOST 1025 @@ -41,6 +43,9 @@ typedef struct stype_s { char *addr; char *port; int sock_fd; +#ifdef WIN32 + HANDLE Event; +#endif struct stype_s *next; } stype_t; diff --git a/server/upsd.c b/server/upsd.c index 2c0d1a1758..ecfeb537ab 100644 --- a/server/upsd.c +++ b/server/upsd.c @@ -27,10 +27,22 @@ #include "netcmds.h" #include "upsconf.h" +#ifndef WIN32 #include #include #include #include +#else +/* Those 2 files for support of getaddrinfo, getnameinfo and freeaddrinfo + on Windows 2000 and older versions */ +#include +#include +/* This override network system calls to adapt to Windows specificity */ +#define W32_NETWORK_CALL_OVERRIDE +#include "wincompat.h" +#undef W32_NETWORK_CALL_OVERRIDE +#include +#endif #include "user.h" #include "nut_ctype.h" @@ -59,7 +71,7 @@ int deny_severity = LOG_WARNING; /* preloaded to STATEPATH in main, can be overridden via upsd.conf */ char *statepath = NULL; - /* preloaded to DATADIR in main, can be overridden via upsd.conf */ + /* preloaded to NUT_DATADIR in main, can be overridden via upsd.conf */ char *datapath = NULL; /* everything else */ @@ -77,6 +89,10 @@ typedef enum { DRIVER = 1, CLIENT, SERVER +#ifdef WIN32 + ,NAMED_PIPE +#endif + } handler_type_t; typedef struct { @@ -84,8 +100,13 @@ typedef struct { void *data; } handler_t; +#ifndef WIN32 /* pollfd */ static struct pollfd *fds = NULL; +#else +static HANDLE *fds = NULL; +static HANDLE mutex = INVALID_HANDLE_VALUE; +#endif static handler_t *handler = NULL; /* pid file */ @@ -178,6 +199,11 @@ void listen_add(const char *addr, const char *port) /* create a listening socket for tcp connections */ static void setuptcp(stype_t *server) { +#ifdef WIN32 + WSADATA WSAdata; + WSAStartup(2,&WSAdata); + atexit((void(*)(void))WSACleanup); +#endif struct addrinfo hints, *res, *ai; int v = 0, one = 1; @@ -215,6 +241,8 @@ static void setuptcp(stype_t *server) continue; } +/* WSAEventSelect automatically set the socket to nonblocking mode */ +#ifndef WIN32 if ((v = fcntl(sock_fd, F_GETFL, 0)) == -1) { fatal_with_errno(EXIT_FAILURE, "setuptcp: fcntl(get)"); } @@ -223,6 +251,7 @@ static void setuptcp(stype_t *server) fatal_with_errno(EXIT_FAILURE, "setuptcp: fcntl(set)"); } +#endif if (listen(sock_fd, 16) < 0) { upsdebug_with_errno(3, "setuptcp: listen"); close(sock_fd); @@ -233,6 +262,16 @@ static void setuptcp(stype_t *server) break; } +#ifdef WIN32 + server->Event = CreateEvent(NULL, /* Security */ + FALSE, /* auto-reset */ + FALSE, /* initial state */ + NULL); /* no name */ + + /* Associate socket event to the socket via its Event object */ + WSAEventSelect( server->sock_fd, server->Event, FD_ACCEPT ); +#endif + freeaddrinfo(res); /* leave up to the caller, server_load(), to fail silently if there is @@ -277,6 +316,10 @@ static void client_disconnect(nut_ctype_t *client) shutdown(client->sock_fd, 2); close(client->sock_fd); +#ifdef WIN32 + CloseHandle(client->Event); +#endif + if (client->loginups) { declogins(client->loginups); } @@ -480,6 +523,15 @@ static void client_connect(stype_t *server) client->addr = xstrdup(inet_ntopW(&csock)); +#ifdef WIN32 + client->Event = CreateEvent(NULL, /*Security,*/ + FALSE, /*auo-reset */ + FALSE, /*initial state*/ + NULL); /* no name */ + + /* Associate socket event to the socket via its Event object */ + WSAEventSelect( client->sock_fd, client->Event, FD_READ ); +#endif pconf_init(&client->ctx, NULL); if (firstclient) { @@ -614,9 +666,17 @@ void driver_free(void) for (ups = firstups; ups; ups = unext) { unext = ups->next; +#ifndef WIN32 if (ups->sock_fd != -1) { close(ups->sock_fd); } +#else + if (ups->sock_fd != INVALID_HANDLE_VALUE) { + DisconnectNamedPipe(ups->sock_fd); + CloseHandle(ups->sock_fd); + ups->sock_fd = INVALID_HANDLE_VALUE; + } +#endif sstate_infofree(ups); sstate_cmdfree(ups); @@ -653,10 +713,18 @@ static void upsd_cleanup(void) free(fds); free(handler); + +#ifdef WIN32 + if(mutex != INVALID_HANDLE_VALUE) { + ReleaseMutex(mutex); + CloseHandle(mutex); + } +#endif } void poll_reload(void) { +#ifndef WIN32 int ret; ret = sysconf(_SC_OPEN_MAX); @@ -670,12 +738,32 @@ void poll_reload(void) fds = xrealloc(fds, maxconn * sizeof(*fds)); handler = xrealloc(handler, maxconn * sizeof(*handler)); +#else + fds = xrealloc(fds, MAXIMUM_WAIT_OBJECTS * sizeof(*fds)); + handler = xrealloc(handler, MAXIMUM_WAIT_OBJECTS * sizeof(*handler)); +#endif +} + +static void set_exit_flag(int sig) +{ + exit_flag = sig; +} + +static void set_reload_flag(int sig) +{ + reload_flag = 1; } /* service requests and check on new data */ static void mainloop(void) { - int i, ret, nfds = 0; + int nfds = 0; +#ifndef WIN32 + int i, ret; +#else + DWORD ret; + pipe_conn_t * conn; +#endif upstype_t *ups; nut_ctype_t *client, *cnext; @@ -690,6 +778,7 @@ static void mainloop(void) reload_flag = 0; } +#ifndef WIN32 /* scan through driver sockets */ for (ups = firstups; ups && (nfds < maxconn); ups = ups->next) { @@ -814,6 +903,140 @@ static void mainloop(void) continue; } } +#else + /* scan through driver sockets */ + for (ups = firstups; ups && (nfds < maxconn); ups = ups->next) { + + /* see if we need to (re)connect to the socket */ + if (ups->sock_fd == INVALID_HANDLE_VALUE) { + ups->sock_fd = sstate_connect(ups); + continue; + } + + /* throw some warnings if it's not feeding us data any more */ + if (sstate_dead(ups, maxage)) { + ups_data_stale(ups); + } else { + ups_data_ok(ups); + } + + if( ups->sock_fd != INVALID_HANDLE_VALUE) { + fds[nfds] = ups->read_overlapped.hEvent; + + handler[nfds].type = DRIVER; + handler[nfds].data = ups; + + nfds++; + } + } + + /* scan through client sockets */ + for (client = firstclient; client; client = cnext) { + + cnext = client->next; + + if (difftime(now, client->last_heard) > 60) { + /* shed clients after 1 minute of inactivity */ + client_disconnect(client); + continue; + } + + if (nfds >= maxconn) { + /* ignore clients that we are unable to handle */ + continue; + } + + fds[nfds] = client->Event; + + handler[nfds].type = CLIENT; + handler[nfds].data = client; + + nfds++; + } + + /* scan through server sockets */ + for (server = firstaddr; server && (nfds < maxconn); server = server->next) { + + if (server->sock_fd < 0) { + continue; + } + + fds[nfds] = server->Event; + + handler[nfds].type = SERVER; + handler[nfds].data = server; + + nfds++; + } + + /* Wait on the read IO on named pipe */ + for (conn = pipe_connhead; conn; conn = conn->next) { + fds[nfds] = conn->overlapped.hEvent; + handler[nfds].type = NAMED_PIPE; + handler[nfds].data = (void *)conn; + nfds++; + } + /* Add the new named pipe connected event */ + fds[nfds] = pipe_connection_overlapped.hEvent; + handler[nfds].type = NAMED_PIPE; + handler[nfds].data = NULL; + nfds++; + + upsdebugx(2, "%s: wait for %d filedescriptors", __func__, nfds); + + ret = WaitForMultipleObjects(nfds,fds,FALSE,2000); + + if (ret == WAIT_TIMEOUT) { + upsdebugx(2, "%s: no data available", __func__); + return; + } + + if (ret == WAIT_FAILED) { + DWORD err = GetLastError(); + err =err; /* remove compile time warning */ + upslog_with_errno(LOG_ERR, "%s", __func__); + return; + } + + switch(handler[ret].type) { + case DRIVER: + sstate_readline((upstype_t *)handler[ret].data); + break; + case CLIENT: + client_readline((nut_ctype_t *)handler[ret].data); + break; + case SERVER: + client_connect((stype_t *)handler[ret].data); + break; + case NAMED_PIPE: + /* a new pipe connection has been signaled */ + if (fds[ret] == pipe_connection_overlapped.hEvent) { + pipe_connect(); + } + /* one of the read event handle has been signaled */ + else { + pipe_conn_t * conn = handler[ret].data; + if ( pipe_ready(conn) ) { + if (!strncmp(conn->buf, SIGCMD_STOP, sizeof(SIGCMD_STOP))) { + set_exit_flag(1); + } + else if (!strncmp(conn->buf, SIGCMD_RELOAD, sizeof(SIGCMD_RELOAD))) { + set_reload_flag(1); + } + else { + upslogx(LOG_ERR,"Unknown signal" + ); + } + + pipe_disconnect(conn); + } + } + break; + default: + upsdebugx(2, "%s: has data available", __func__); + break; + } +#endif } static void help(const char *progname) @@ -838,18 +1061,9 @@ static void help(const char *progname) exit(EXIT_SUCCESS); } -static void set_reload_flag(int sig) -{ - reload_flag = 1; -} - -static void set_exit_flag(int sig) -{ - exit_flag = sig; -} - static void setup_signals(void) { +#ifndef WIN32 struct sigaction sa; sigemptyset(&sa.sa_mask); @@ -869,10 +1083,14 @@ static void setup_signals(void) /* handle reloading */ sa.sa_handler = set_reload_flag; sigaction(SIGHUP, &sa, NULL); +#else + pipe_create(UPSD_PIPE_NAME); +#endif } void check_perms(const char *fn) { +#ifndef WIN32 int ret; struct stat st; @@ -886,11 +1104,17 @@ void check_perms(const char *fn) if (st.st_mode & (S_IROTH | S_IXOTH)) { upslogx(LOG_WARNING, "%s is world readable", fn); } +#endif } int main(int argc, char **argv) { - int i, cmd = 0; + int i; +#ifndef WIN32 + int cmd = 0; +#else + const char * cmd = NULL; +#endif char *chroot_path = NULL; const char *user = RUN_AS_USER; struct passwd *new_uid = NULL; @@ -899,7 +1123,26 @@ int main(int argc, char **argv) /* yes, xstrdup - the conf handlers call free on this later */ statepath = xstrdup(dflt_statepath()); - datapath = xstrdup(DATADIR); +#ifndef WIN32 + datapath = xstrdup(NUT_DATADIR); +#else + datapath = getfullpath(PATH_SHARE); + + /* remove trailing .exe */ + char * drv_name; + drv_name = (char *)xbasename(argv[0]); + char * name = strrchr(drv_name,'.'); + if( name != NULL ) { + if(strcasecmp(name, ".exe") == 0 ) { + progname = strdup(drv_name); + char * t = strrchr(progname,'.'); + *t = 0; + } + } + else { + progname = drv_name; + } +#endif /* set up some things for later */ snprintf(pidfn, sizeof(pidfn), "%s/%s.pid", altpidpath(), progname); @@ -959,14 +1202,29 @@ int main(int argc, char **argv) } if (cmd) { +#ifndef WIN32 sendsignalfn(pidfn, cmd); +#else + sendsignal(UPSD_PIPE_NAME,cmd); +#endif exit(EXIT_SUCCESS); } /* otherwise, we are being asked to start. * so check if a previous instance is running by sending signal '0' * (Ie 'kill 0') */ +#ifndef WIN32 if (sendsignalfn(pidfn, 0) == 0) { +#else + mutex = CreateMutex(NULL,TRUE,UPSD_PIPE_NAME); + if(mutex == NULL ) { + if( GetLastError() != ERROR_ACCESS_DENIED ) { + fatalx(EXIT_FAILURE, "Can not create mutex %s : %d.\n",UPSD_PIPE_NAME,(int)GetLastError()); + } + } + + if (GetLastError() == ERROR_ALREADY_EXISTS || GetLastError() == ERROR_ACCESS_DENIED) { +#endif printf("Fatal error: A previous upsd instance is already running!\n"); printf("Either stop the previous instance first, or use the 'reload' command.\n"); exit(EXIT_FAILURE); @@ -995,8 +1253,12 @@ int main(int argc, char **argv) chroot_start(chroot_path); } +#ifndef WIN32 /* default to system limit (may be overridden in upsd.conf */ maxconn = sysconf(_SC_OPEN_MAX); +#else + maxconn = 64; /*FIXME : arbitrary value, need adjustement */ +#endif /* handle upsd.conf */ load_upsdconf(0); /* 0 = initial */ @@ -1008,10 +1270,11 @@ int main(int argc, char **argv) ssl_init(); become_user(new_uid); - +#ifndef WIN32 if (chdir(statepath)) { fatal_with_errno(EXIT_FAILURE, "Can't chdir to %s", statepath); } +#endif /* check statepath perms */ check_perms(statepath); diff --git a/server/upsd.h b/server/upsd.h index 2b3a3a74d1..626f7debbe 100644 --- a/server/upsd.h +++ b/server/upsd.h @@ -31,9 +31,11 @@ #include "common.h" +#ifndef WIN32 #include #include #include +#endif #include "timehead.h" @@ -76,9 +78,13 @@ extern upstype_t *firstups; extern nut_ctype_t *firstclient; /* map commands onto signals */ - +#ifndef WIN32 #define SIGCMD_STOP SIGTERM #define SIGCMD_RELOAD SIGHUP +#else +#define SIGCMD_STOP COMMAND_STOP +#define SIGCMD_RELOAD COMMAND_RELOAD +#endif /* awkward way to make a string out of a numeric constant */ diff --git a/server/upstype.h b/server/upstype.h index bbadd500ad..5342e98558 100644 --- a/server/upstype.h +++ b/server/upstype.h @@ -36,7 +36,13 @@ typedef struct upstype_s { char *fn; char *desc; +#ifndef WIN32 int sock_fd; +#else + HANDLE sock_fd; + char buf[SMALLBUF]; + OVERLAPPED read_overlapped; +#endif int stale; int dumpdone; int data_ok; diff --git a/server/user.c b/server/user.c index 8eff8a06b6..f10e65c8b9 100644 --- a/server/user.c +++ b/server/user.c @@ -18,9 +18,11 @@ */ #include +#ifndef WIN32 #include #include #include +#endif #include "common.h" #include "parseconf.h" diff --git a/tools/nut-scanner/.gitignore b/tools/nut-scanner/.gitignore index 29db0a024e..a5e77c1bc9 100644 --- a/tools/nut-scanner/.gitignore +++ b/tools/nut-scanner/.gitignore @@ -5,3 +5,4 @@ nut-scanner nutscan-snmp.h nutscan-usb.h test-nutscan +*.exe diff --git a/tools/nut-scanner/Makefile.am b/tools/nut-scanner/Makefile.am index 56278eed26..5f0c3a5d89 100644 --- a/tools/nut-scanner/Makefile.am +++ b/tools/nut-scanner/Makefile.am @@ -14,14 +14,16 @@ libnutscan_la_SOURCES = scan_nut.c scan_ipmi.c \ scan_avahi.c scan_eaton_serial.c nutscan-serial.c \ $(top_srcdir)/drivers/serial.c \ $(top_srcdir)/drivers/bcmxcp_ser.c \ - $(top_srcdir)/common/common.c + $(top_srcdir)/common/common.c \ + $(top_srcdir)/common/wincompat.c libnutscan_la_LIBADD = $(NETLIBS) $(LIBLTDL_LIBS) libnutscan_la_LDFLAGS = $(SERLIBS) -version-info 1:0:0 libnutscan_la_CFLAGS = -I$(top_srcdir)/clients -I$(top_srcdir)/include $(LIBLTDL_CFLAGS) -I$(top_srcdir)/drivers -nut_scanner_SOURCES = nut-scanner.c +nut_scanner_SOURCES = nut-scanner.c $(top_srcdir)/common/common.c \ + $(top_srcdir)/common/wincompat.c nut_scanner_CFLAGS = -I$(top_srcdir)/clients -I$(top_srcdir)/include -nut_scanner_LDADD = libnutscan.la ../../common/libcommon.la +nut_scanner_LDADD = libnutscan.la if WITH_SSL libnutscan_la_CFLAGS += $(LIBSSL_CFLAGS) diff --git a/tools/nut-scanner/nut-scanner.c b/tools/nut-scanner/nut-scanner.c index 445115884f..b52b74fd10 100644 --- a/tools/nut-scanner/nut-scanner.c +++ b/tools/nut-scanner/nut-scanner.c @@ -141,6 +141,9 @@ int printq(int quiet,const char *fmt, ...) va_start(ap, fmt); ret = vprintf(fmt, ap); va_end(ap); + va_start(ap, fmt); + ret = vprintf(fmt, ap); + va_end(ap); return ret; } diff --git a/tools/nut-scanner/nutscan-init.c b/tools/nut-scanner/nutscan-init.c index 16ea760da3..9ec6799f28 100644 --- a/tools/nut-scanner/nutscan-init.c +++ b/tools/nut-scanner/nutscan-init.c @@ -34,6 +34,11 @@ int nutscan_load_avahi_library(void); int nutscan_load_ipmi_library(void); int nutscan_load_upsclient_library(void); +#ifdef WIN32 +/* Stub for libupsclient */ +void do_upsconf_args(char *confupsname, char *var, char *val) {;}; +#endif + void nutscan_init(void) { #ifdef WITH_USB diff --git a/tools/nut-scanner/nutscan-ip.c b/tools/nut-scanner/nutscan-ip.c index 8788018273..04b90b7dc3 100644 --- a/tools/nut-scanner/nutscan-ip.c +++ b/tools/nut-scanner/nutscan-ip.c @@ -21,8 +21,17 @@ #include #include "common.h" #include +#ifndef WIN32 #include #include +#else +/* Those 2 files for support of getaddrinfo, getnameinfo and freeaddrinfo + on Windows 2000 and older versions */ +#include +#include +#define AI_NUMERICSERV NI_NUMERICSERV +#include "wincompat.h" +#endif static void increment_IPv6(struct in6_addr * addr) { diff --git a/tools/nut-scanner/nutscan-ip.h b/tools/nut-scanner/nutscan-ip.h index b28ff0062b..a3f403f280 100644 --- a/tools/nut-scanner/nutscan-ip.h +++ b/tools/nut-scanner/nutscan-ip.h @@ -19,8 +19,12 @@ #ifndef SCAN_IP #define SCAN_IP +#ifndef WIN32 #include #include +#else +#include +#endif #ifdef __cplusplus /* *INDENT-OFF* */ diff --git a/tools/nut-scanner/nutscan-serial.c b/tools/nut-scanner/nutscan-serial.c index 65e0c03788..600860a2d6 100644 --- a/tools/nut-scanner/nutscan-serial.c +++ b/tools/nut-scanner/nutscan-serial.c @@ -22,6 +22,9 @@ #include #include #include "nut_platform.h" +#ifdef WIN32 +#include "wincompat.h" +#endif #ifdef WIN32 /* Windows: all serial port names start with "COM" */ diff --git a/tools/nut-scanner/scan_eaton_serial.c b/tools/nut-scanner/scan_eaton_serial.c index ecfbe1498b..ac95cc9976 100644 --- a/tools/nut-scanner/scan_eaton_serial.c +++ b/tools/nut-scanner/scan_eaton_serial.c @@ -68,7 +68,11 @@ static pthread_mutex_t dev_mutex; /* Fake driver main, for using serial functions, needed for bcmxcp_ser.c */ char *device_path; +#ifndef WIN32 int upsfd; +#else +HANDLE upsfd; +#endif int exit_flag = 0; int do_lock_port; @@ -110,7 +114,7 @@ unsigned char calc_checksum(const unsigned char *buf) /* Light version of of drivers/libshut.c->shut_synchronise() * return 1 if OK, 0 otherwise */ -int shut_synchronise(int upsfd) +int shut_synchronise(TYPE_FD upsfd) { int try; u_char reply = '\0'; @@ -136,9 +140,9 @@ int shut_synchronise(int upsfd) nutscan_device_t * nutscan_scan_eaton_serial_shut(const char* port_name) { nutscan_device_t * dev = NULL; - int devfd = -1; + TYPE_FD devfd = ERROR_FD; - if ( (devfd = ser_open_nf(port_name)) != -1 ) { + if ( (devfd = ser_open_nf(port_name)) != ERROR_FD ) { /* set RTS to off and DTR to on to allow correct behavior * with UPS using PnP feature */ if (ser_set_dtr(devfd, 1) != -1) { @@ -185,13 +189,14 @@ nutscan_device_t * nutscan_scan_eaton_serial_shut(const char* port_name) nutscan_device_t * nutscan_scan_eaton_serial_xcp(const char* port_name) { nutscan_device_t * dev = NULL; - int i, ret, devfd = -1; + int i, ret; + TYPE_FD devfd = ERROR_FD; unsigned char answer[256]; unsigned char sbuf[128]; memset(sbuf, 0, 128); - if ( (devfd = ser_open_nf(port_name)) != -1 ) { + if ( (devfd = ser_open_nf(port_name)) != ERROR_FD ) { #ifdef HAVE_PTHREAD pthread_mutex_lock(&dev_mutex); #endif @@ -276,10 +281,10 @@ nutscan_device_t * nutscan_scan_eaton_serial_q1(const char* port_name) nutscan_device_t * dev = NULL; struct termios tio; int ret = 0, retry; - int devfd = -1; + TYPE_FD devfd = ERROR_FD; char buf[128]; - if ( (devfd = ser_open_nf(port_name)) != -1 ) { + if ( (devfd = ser_open_nf(port_name)) != ERROR_FD ) { if (ser_set_speed_nf(devfd, port_name, B2400) != -1) { if (!tcgetattr(devfd, &tio)) { @@ -371,8 +376,10 @@ static void * nutscan_scan_eaton_serial_device(void * port_arg) nutscan_device_t * nutscan_scan_eaton_serial(const char* ports_range) { +#ifndef WIN32 struct sigaction oldact; int change_action_handler = 0; +#endif char *current_port_name = NULL; char **serial_ports_list; int current_port_nb; @@ -391,6 +398,7 @@ nutscan_device_t * nutscan_scan_eaton_serial(const char* ports_range) return NULL; } +#ifndef WIN32 /* Ignore SIGPIPE if the caller hasn't set a handler for it yet */ if( sigaction(SIGPIPE, NULL, &oldact) == 0 ) { if( oldact.sa_handler == SIG_DFL ) { @@ -398,6 +406,7 @@ nutscan_device_t * nutscan_scan_eaton_serial(const char* ports_range) signal(SIGPIPE,SIG_IGN); } } +#endif /* port(s) iterator */ current_port_nb = 0; @@ -424,9 +433,11 @@ nutscan_device_t * nutscan_scan_eaton_serial(const char* ports_range) free(thread_array); #endif +#ifndef WIN32 if(change_action_handler) { signal(SIGPIPE,SIG_DFL); } +#endif /* free everything... */ i=0; diff --git a/tools/nut-scanner/scan_nut.c b/tools/nut-scanner/scan_nut.c index 005cfe6a76..a2b26054e3 100644 --- a/tools/nut-scanner/scan_nut.c +++ b/tools/nut-scanner/scan_nut.c @@ -32,11 +32,11 @@ static const char *dl_error = NULL; static int (*nut_upscli_splitaddr)(const char *buf,char **hostname, int *port); static int (*nut_upscli_tryconnect)(UPSCONN_t *ups, const char *host, int port, - int flags,struct timeval * timeout); + int flags,struct timeval * timeout); static int (*nut_upscli_list_start)(UPSCONN_t *ups, unsigned int numq, - const char **query); + const char **query); static int (*nut_upscli_list_next)(UPSCONN_t *ups, unsigned int numq, - const char **query,unsigned int *numa, char ***answer); + const char **query,unsigned int *numa, char ***answer); static int (*nut_upscli_disconnect)(UPSCONN_t *ups); static nutscan_device_t * dev_ret = NULL; @@ -53,51 +53,51 @@ struct scan_nut_arg { int nutscan_load_upsclient_library() { - if( dl_handle != NULL ) { - /* if previous init failed */ - if( dl_handle == (void *)1 ) { - return 0; - } - /* init has already been done */ - return 1; - } + if( dl_handle != NULL ) { + /* if previous init failed */ + if( dl_handle == (void *)1 ) { + return 0; + } + /* init has already been done */ + return 1; + } - if( lt_dlinit() != 0 ) { - fprintf(stderr, "Error initializing lt_init\n"); - return 0; - } + if( lt_dlinit() != 0 ) { + fprintf(stderr, "Error initializing lt_init\n"); + return 0; + } - dl_handle = lt_dlopenext(libname); - if (!dl_handle) { - dl_error = lt_dlerror(); - goto err; - } + dl_handle = lt_dlopenext(libname); + if (!dl_handle) { + dl_error = lt_dlerror(); + goto err; + } - lt_dlerror(); /* Clear any existing error */ + lt_dlerror(); /* Clear any existing error */ - *(void **) (&nut_upscli_splitaddr) = lt_dlsym(dl_handle, - "upscli_splitaddr"); - if ((dl_error = lt_dlerror()) != NULL) { - goto err; - } + *(void **) (&nut_upscli_splitaddr) = lt_dlsym(dl_handle, + "upscli_splitaddr"); + if ((dl_error = lt_dlerror()) != NULL) { + goto err; + } - *(void **) (&nut_upscli_tryconnect) = lt_dlsym(dl_handle, - "upscli_tryconnect"); - if ((dl_error = lt_dlerror()) != NULL) { - goto err; - } + *(void **) (&nut_upscli_tryconnect) = lt_dlsym(dl_handle, + "upscli_tryconnect"); + if ((dl_error = lt_dlerror()) != NULL) { + goto err; + } - *(void **) (&nut_upscli_list_start) = lt_dlsym(dl_handle, - "upscli_list_start"); - if ((dl_error = lt_dlerror()) != NULL) { - goto err; - } + *(void **) (&nut_upscli_list_start) = lt_dlsym(dl_handle, + "upscli_list_start"); + if ((dl_error = lt_dlerror()) != NULL) { + goto err; + } - *(void **) (&nut_upscli_list_next) = lt_dlsym(dl_handle, - "upscli_list_next"); - if ((dl_error = lt_dlerror()) != NULL) { - goto err; - } + *(void **) (&nut_upscli_list_next) = lt_dlsym(dl_handle, + "upscli_list_next"); + if ((dl_error = lt_dlerror()) != NULL) { + goto err; + } *(void **) (&nut_upscli_disconnect) = lt_dlsym(dl_handle, "upscli_disconnect"); @@ -107,10 +107,10 @@ int nutscan_load_upsclient_library() return 1; err: - fprintf(stderr, "Cannot load NUT library (%s) : %s. NUT search disabled.\n", libname, dl_error); - dl_handle = (void *)1; + fprintf(stderr, "Cannot load NUT library (%s) : %s. NUT search disabled.\n", libname, dl_error); + dl_handle = (void *)1; lt_dlexit(); - return 0; + return 0; } /* FIXME: SSL support */ @@ -205,10 +205,17 @@ nutscan_device_t * nutscan_scan_nut(const char* startIP, const char* stopIP, con char * ip_str = NULL; char * ip_dest = NULL; char buf[SMALLBUF]; - struct sigaction oldact; - int change_action_handler = 0; int i; +#ifndef WIN32 + struct sigaction oldact; +#endif struct scan_nut_arg *nut_arg; +#ifdef WIN32 + WSADATA WSAdata; + WSAStartup(2,&WSAdata); + atexit((void(*)(void))WSACleanup); +#endif + #ifdef HAVE_PTHREAD pthread_t thread; pthread_t * thread_array = NULL; @@ -221,6 +228,8 @@ nutscan_device_t * nutscan_scan_nut(const char* startIP, const char* stopIP, con return NULL; } +#ifndef WIN32 + int change_action_handler = 0; /* Ignore SIGPIPE if the caller hasn't set a handler for it yet */ if( sigaction(SIGPIPE, NULL, &oldact) == 0 ) { if( oldact.sa_handler == SIG_DFL ) { @@ -228,6 +237,7 @@ nutscan_device_t * nutscan_scan_nut(const char* startIP, const char* stopIP, con signal(SIGPIPE,SIG_IGN); } } +#endif ip_str = nutscan_ip_iter_init(&ip,startIP,stopIP); @@ -276,9 +286,11 @@ nutscan_device_t * nutscan_scan_nut(const char* startIP, const char* stopIP, con free(thread_array); #endif +#ifndef WIN32 if(change_action_handler) { signal(SIGPIPE,SIG_DFL); } +#endif return nutscan_rewind_device(dev_ret); } diff --git a/tools/nut-scanner/scan_snmp.c b/tools/nut-scanner/scan_snmp.c index 0fcb9a6d8c..3c432529ec 100644 --- a/tools/nut-scanner/scan_snmp.c +++ b/tools/nut-scanner/scan_snmp.c @@ -22,7 +22,12 @@ #ifdef WITH_SNMP +#ifndef WIN32 #include +#else +#undef _WIN32_WINNT +#endif + #include #include #include @@ -654,6 +659,13 @@ nutscan_device_t * nutscan_scan_snmp(const char * start_ip, const char * stop_ip nutscan_ip_iter_t ip; char * ip_str = NULL; #ifdef HAVE_PTHREAD + +#ifdef WIN32 + WSADATA WSAdata; + WSAStartup(2,&WSAdata); + atexit((void(*)(void))WSACleanup); +#endif + pthread_t thread; pthread_mutex_init(&dev_mutex,NULL); diff --git a/tools/nut-scanner/scan_usb.c b/tools/nut-scanner/scan_usb.c index c82dad58c5..065e67a587 100644 --- a/tools/nut-scanner/scan_usb.c +++ b/tools/nut-scanner/scan_usb.c @@ -37,7 +37,10 @@ static char * (*nut_usb_strerror)(void); static void (*nut_usb_init)(void); static int (*nut_usb_get_string_simple)(usb_dev_handle *dev, int index, char *buf, size_t buflen); +#ifndef WIN32 static struct usb_bus * (*nut_usb_busses); +#endif +static struct usb_bus * (*nut_usb_get_busses)(); static usb_dev_handle * (*nut_usb_open)(struct usb_device *dev); static int (*nut_usb_find_devices)(void); @@ -90,10 +93,17 @@ int nutscan_load_usb_library() goto err; } +#ifndef WIN32 *(void **) (&nut_usb_busses) = lt_dlsym(dl_handle, "usb_busses"); if ((dl_error = lt_dlerror()) != NULL) { goto err; } +#endif + *(void **) (&nut_usb_get_busses) = lt_dlsym(dl_handle, + "usb_get_busses"); + if ((dl_error = lt_dlerror()) != NULL) { + goto err; + } *(void **) (&nut_usb_open) = lt_dlsym(dl_handle, "usb_open"); if ((dl_error = lt_dlerror()) != NULL) { @@ -155,7 +165,11 @@ nutscan_device_t * nutscan_scan_usb() (*nut_usb_find_busses)(); (*nut_usb_find_devices)(); +#ifndef WIN32 for (bus = (*nut_usb_busses); bus; bus = bus->next) { +#else + for (bus = (*nut_usb_get_busses)(); bus; bus = bus->next) { +#endif for (dev = bus->devices; dev; dev = dev->next) { if ((driver_name = is_usb_device_supported(usb_device_table, diff --git a/tools/nut-scanner/scan_xml_http.c b/tools/nut-scanner/scan_xml_http.c index ee713e4543..cf16c87225 100644 --- a/tools/nut-scanner/scan_xml_http.c +++ b/tools/nut-scanner/scan_xml_http.c @@ -20,14 +20,24 @@ #include "common.h" #include "nut-scan.h" #ifdef WITH_NEON +#ifndef WIN32 #include #include #include #include #include +#include +#define SOCK_OPT_CAST +#else +#define SOCK_OPT_CAST (char*) +/* Those 2 files for support of getaddrinfo, getnameinfo and freeaddrinfo + on Windows 2000 and older versions */ +#include +#include +#endif + #include #include -#include #include #include #include @@ -131,6 +141,12 @@ nutscan_device_t * nutscan_scan_xml_http(long usec_timeout) char string[SMALLBUF]; ssize_t recv_size; +#ifdef WIN32 + WSADATA WSAdata; + WSAStartup(2,&WSAdata); + atexit((void(*)(void))WSACleanup); +#endif + nutscan_device_t * nut_dev = NULL; nutscan_device_t * current_nut_dev = NULL; @@ -144,7 +160,7 @@ nutscan_device_t * nutscan_scan_xml_http(long usec_timeout) sockAddress.sin_family = AF_INET; sockAddress.sin_addr.s_addr = INADDR_BROADCAST; sockAddress.sin_port = htons(port); - setsockopt(peerSocket, SOL_SOCKET, SO_BROADCAST, &sockopt_on, + setsockopt(peerSocket, SOL_SOCKET, SO_BROADCAST, SOCK_OPT_CAST&sockopt_on, sizeof(sockopt_on)); /* Send scan request */