Created the Validate AuthToken Daemon. There is still work to be done on
this component.
This commit is contained in:
		| @@ -32,6 +32,9 @@ package package-clean package-install package-uninstall: | |||||||
|  |  | ||||||
| clean-local: | clean-local: | ||||||
| 	if [ -d lib ]; then  rm -rf lib; fi | 	if [ -d lib ]; then  rm -rf lib; fi | ||||||
|  | 	if [ -d bin ]; then  rm -rf bin; fi | ||||||
|  | 	if [ -d lib64 ]; then  rm -rf lib64; fi | ||||||
|  | 	if [ -d bin64 ]; then  rm -rf bin64; fi | ||||||
|  |  | ||||||
| maintainer-clean-local: | maintainer-clean-local: | ||||||
| 	rm -f Makefile.in | 	rm -f Makefile.in | ||||||
|   | |||||||
| @@ -127,13 +127,16 @@ AM_CONDITIONAL(WINDOWS, test "$TARGET_OS" = "windows") | |||||||
| case $target_cpu in | case $target_cpu in | ||||||
|     x86_64|p*pc64|s390x) |     x86_64|p*pc64|s390x) | ||||||
|         LIB=lib64 |         LIB=lib64 | ||||||
|  | 	BIN=bin64 | ||||||
|     ;; |     ;; | ||||||
|     *ia64|*) |     *ia64|*) | ||||||
|         LIB=lib |         LIB=lib | ||||||
|  | 	BIN=bin | ||||||
|     ;; |     ;; | ||||||
| esac | esac | ||||||
|  |  | ||||||
| AC_SUBST(LIB) | AC_SUBST(LIB) | ||||||
|  | AC_SUBST(BIN) | ||||||
| AM_CONDITIONAL(LIB64, test "$LIB" = lib64) | AM_CONDITIONAL(LIB64, test "$LIB" = lib64) | ||||||
|  |  | ||||||
| # | # | ||||||
| @@ -276,6 +279,8 @@ server/AuthTokenValidate/linux/Makefile | |||||||
| server/AuthTokenValidate/idenTokenProviders/Makefile | server/AuthTokenValidate/idenTokenProviders/Makefile | ||||||
| server/AuthTokenValidate/idenTokenProviders/casa/Makefile | server/AuthTokenValidate/idenTokenProviders/casa/Makefile | ||||||
| server/AuthTokenValidate/idenTokenProviders/casa/linux/Makefile | server/AuthTokenValidate/idenTokenProviders/casa/linux/Makefile | ||||||
|  | server/AuthTokenValidate/Svc/Makefile | ||||||
|  | server/AuthTokenValidate/Svc/linux/Makefile | ||||||
| server/PamSupport/Makefile | server/PamSupport/Makefile | ||||||
| server/PamSupport/linux/Makefile | server/PamSupport/linux/Makefile | ||||||
| server/ApacheSupport/Makefile | server/ApacheSupport/Makefile | ||||||
|   | |||||||
| @@ -20,9 +20,9 @@ | |||||||
| # | # | ||||||
| ####################################################################### | ####################################################################### | ||||||
|  |  | ||||||
| SUBDIRS = $(TARGET_OS) idenTokenProviders | SUBDIRS = $(TARGET_OS) idenTokenProviders Svc | ||||||
|  |  | ||||||
| DIST_SUBDIRS = linux idenTokenProviders | DIST_SUBDIRS = linux idenTokenProviders Svc | ||||||
|  |  | ||||||
| CFILES = | CFILES = | ||||||
|  |  | ||||||
|   | |||||||
| @@ -0,0 +1,37 @@ | |||||||
|  | ####################################################################### | ||||||
|  | # | ||||||
|  | #  Copyright (C) 2006 Novell, Inc. | ||||||
|  | # | ||||||
|  | #  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. | ||||||
|  | # | ||||||
|  | #  Author: Juan Carlos Luciani <jluciani@novell.com> | ||||||
|  | # | ||||||
|  | ####################################################################### | ||||||
|  |  | ||||||
|  | SUBDIRS = $(TARGET_OS) | ||||||
|  |  | ||||||
|  | DIST_SUBDIRS = linux | ||||||
|  |  | ||||||
|  | CFILES = | ||||||
|  |  | ||||||
|  | EXTRA_DIST = $(CFILES) *.h | ||||||
|  |  | ||||||
|  | .PHONY: package package-clean package-install package-uninstall | ||||||
|  | package package-clean package-install package-uninstall: | ||||||
|  | 	$(MAKE) -C $(TARGET_OS) $@ | ||||||
|  |  | ||||||
|  | maintainer-clean-local: | ||||||
|  | 	rm -f Makefile.in | ||||||
|  |  | ||||||
| @@ -0,0 +1,116 @@ | |||||||
|  | ####################################################################### | ||||||
|  | # | ||||||
|  | #  Copyright (C) 2006 Novell, Inc. | ||||||
|  | # | ||||||
|  | #  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. | ||||||
|  | # | ||||||
|  | #  Author: Juan Carlos Luciani <jluciani@novell.com> | ||||||
|  | # | ||||||
|  | ####################################################################### | ||||||
|  |  | ||||||
|  | if DEBUG | ||||||
|  | TARGET_CFG = Debug | ||||||
|  | CFLAGS += -v -w | ||||||
|  | DEFINES = -DDBG | ||||||
|  | else | ||||||
|  | TARGET_CFG = Release | ||||||
|  | DEFINES = -DNDEBUG | ||||||
|  | endif | ||||||
|  |  | ||||||
|  | # Override the link setting for C++ | ||||||
|  | LINK = g++ | ||||||
|  |  | ||||||
|  | SUBDIRS = | ||||||
|  |  | ||||||
|  | DIST_SUBDIRS = | ||||||
|  |  | ||||||
|  | ROOT = ../../../.. | ||||||
|  |  | ||||||
|  | LIBDIR = $(ROOT)/$(LIB) | ||||||
|  | BINDIR = $(ROOT)/$(BIN) | ||||||
|  |  | ||||||
|  | # handle Mono secondary dependencies | ||||||
|  | export MONO_PATH := $(MONO_PATH) | ||||||
|  |  | ||||||
|  | MODULE_NAME = CasaAuthtokenValidateD | ||||||
|  |  | ||||||
|  | CFILES = | ||||||
|  |  | ||||||
|  | CPPFILES = server.cpp | ||||||
|  |  | ||||||
|  | CSFILES_CSC := | ||||||
|  | INCLUDES = -I. -I$(ROOT)/include | ||||||
|  | RESOURCES = | ||||||
|  | DEFINES += -Wno-format-extra-args -fno-strict-aliasing -fshort-wchar  | ||||||
|  | CFLAGS += $(INCLUDES) $(DEFINES) | ||||||
|  | CPPFLAGS += -fPIC $(INCLUDES) $(DEFINES) | ||||||
|  | LIBS = -lpthread -lcasa_s_ipc | ||||||
|  | LDFLAGS = -L$(LIBDIR)/$(TARGET_CFG) | ||||||
|  |  | ||||||
|  | OBJDIR = ./$(TARGET_CFG)/$(LIB) | ||||||
|  | OBJS = $(addprefix $(OBJDIR)/, $(CFILES:%.c=%.o)) $(addprefix $(OBJDIR)/, $(CPPFILES:%.cpp=%.o)) | ||||||
|  |  | ||||||
|  | EXTRA_DIST = $(CFILES) *.h | ||||||
|  |  | ||||||
|  | CUR_DIR := $(shell pwd) | ||||||
|  |  | ||||||
|  | all: $(OBJDIR)/$(MODULE_NAME) | ||||||
|  |  | ||||||
|  | # | ||||||
|  | # Pattern based rules. | ||||||
|  | # | ||||||
|  | vpath %.c $(CLIENT) $(COMMON) | ||||||
|  | vpath %.cpp $(CLIENT) $(COMMON) | ||||||
|  |  | ||||||
|  | $(OBJDIR)/%.o: %.c | ||||||
|  | 	$(CC) -c $(CFLAGS) -o $@ $< | ||||||
|  |  | ||||||
|  | $(OBJDIR)/%.o: %.cpp | ||||||
|  | 	$(CC) -c $(CPPFLAGS) -o $@ $< | ||||||
|  |  | ||||||
|  | $(OBJDIR)/$(MODULE_NAME): $(OBJDIR) $(OBJS) | ||||||
|  | 	@echo [======== Linking $@ ========] | ||||||
|  | 	$(LINK) -o $@ $(LDFLAGS) $(OBJS) $(LIBS) | ||||||
|  | 	cp -f $(OBJDIR)/$(MODULE_NAME) $(BINDIR)/$(TARGET_CFG)/$(MODULE_NAME) | ||||||
|  |  | ||||||
|  | $(OBJDIR): | ||||||
|  | 	[ -d $(OBJDIR) ] || mkdir -p $(OBJDIR) | ||||||
|  | 	[ -d $(LIBDIR) ] || mkdir -p $(LIBDIR) | ||||||
|  | 	[ -d $(LIBDIR)/$(TARGET_CFG) ] || mkdir -p $(LIBDIR)/$(TARGET_CFG) | ||||||
|  | 	[ -d $(BINDIR) ] || mkdir -p $(BINDIR) | ||||||
|  | 	[ -d $(BINDIR)/$(TARGET_CFG) ] || mkdir -p $(BINDIR)/$(TARGET_CFG) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | install-exec-local: $(OBJDIR)/$(MODULE_NAME) | ||||||
|  | 	$(mkinstalldirs) $(DESTDIR)$(libdir) | ||||||
|  | 	$(INSTALL_PROGRAM) $(OBJDIR)/$(MODULE_NAME) $(DESTDIR)$(libdir)/ | ||||||
|  |  | ||||||
|  | uninstall-local: | ||||||
|  | 	cd $(DESTDIR)$(libdir); rm -f $(OBJDIR)/$(MODULE_NAME) | ||||||
|  | 	rmdir $(DESTDIR)$(libdir) | ||||||
|  |  | ||||||
|  | #installcheck-local: install | ||||||
|  | #	$(mkinstalldirs) $(DESTDIR)$(libdir) | ||||||
|  | #	$(INSTALL_PROGRAM) $(DESTDIR)$(libdir) | ||||||
|  | #	cd $(DESTDIR)$(libdir); $(MONO) | ||||||
|  |  | ||||||
|  | clean-local: | ||||||
|  | 	if [ -d $(TARGET_CFG) ]; then  rm -rf $(TARGET_CFG); fi | ||||||
|  |  | ||||||
|  | distclean-local: | ||||||
|  |  | ||||||
|  | maintainer-clean-local: | ||||||
|  | 	rm -f Makefile.in | ||||||
|  |  | ||||||
| @@ -0,0 +1,148 @@ | |||||||
|  | /**********************\************************************************* | ||||||
|  |  *  | ||||||
|  |  *  Copyright (C) 2006 Novell, Inc. All Rights Reserved. | ||||||
|  |  * | ||||||
|  |  *  This library is free software; you can redistribute it and/or | ||||||
|  |  *  modify it under the terms of the GNU Lesser General Public | ||||||
|  |  *  License as published by the Free Software Foundation; version 2.1 | ||||||
|  |  *  of the License. | ||||||
|  |  * | ||||||
|  |  *  This library 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 | ||||||
|  |  *  Library Lesser General Public License for more details. | ||||||
|  |  * | ||||||
|  |  *  You should have received a copy of the GNU Lesser General Public | ||||||
|  |  *  License along with this library; if not, Novell, Inc. | ||||||
|  |  *  | ||||||
|  |  *  To contact Novell about this file by physical or electronic mail,  | ||||||
|  |  *  you may find current contact information at www.novell.com. | ||||||
|  |  *  | ||||||
|  |  *  Author: Juan Carlos Luciani <jluciani@novell.com> | ||||||
|  |  * | ||||||
|  |  ***********************************************************************/ | ||||||
|  |  | ||||||
|  | #ifndef _IPCINT_ | ||||||
|  | #define _IPCINT_ | ||||||
|  |  | ||||||
|  | //===[ Include files ]===================================================== | ||||||
|  |  | ||||||
|  | #include <iostream> | ||||||
|  | #include <string> | ||||||
|  | #include <list> | ||||||
|  | #include <map> | ||||||
|  | #include <vector> | ||||||
|  | using namespace std; | ||||||
|  |  | ||||||
|  | extern "C" { | ||||||
|  | #include <stdint.h> | ||||||
|  | #include <unistd.h> | ||||||
|  | #include <stdio.h> | ||||||
|  | #include <sys/types.h> | ||||||
|  | #include <sys/wait.h> | ||||||
|  | #include <errno.h> | ||||||
|  | #include <string.h> | ||||||
|  | #include <stdlib.h> | ||||||
|  | #include <pthread.h> | ||||||
|  | #include <syslog.h> | ||||||
|  | #include <signal.h> | ||||||
|  | #include <sys/stat.h> | ||||||
|  | #include <sys/file.h> | ||||||
|  | #include <assert.h>     // Ensure that NDEBUG is defined for release builds! | ||||||
|  | #include <sys/ipc.h> | ||||||
|  | #include <casa_s_ipc.h> | ||||||
|  | } | ||||||
|  |  | ||||||
|  | //===[ External data ]===================================================== | ||||||
|  |  | ||||||
|  | extern int DebugLevel; | ||||||
|  | extern bool UseSyslog; | ||||||
|  | extern char appName[]; | ||||||
|  | extern char *pAppName; | ||||||
|  | extern pthread_mutex_t interlockedMutex; | ||||||
|  |  | ||||||
|  | //===[ Macro definitions ]================================================= | ||||||
|  |  | ||||||
|  | // | ||||||
|  | // DbgTrace macro define | ||||||
|  | // | ||||||
|  | #define MAX_FORMAT_STRING_LEN 1024 | ||||||
|  | #define DbgTrace(LEVEL, X, Y) {                                      \ | ||||||
|  |    if (LEVEL == 0 || DebugLevel >= LEVEL) {                          \ | ||||||
|  |       if (UseSyslog)                                                 \ | ||||||
|  |          syslog(LOG_USER | LOG_INFO, X, Y);                          \ | ||||||
|  |       else {                                                         \ | ||||||
|  |          char *pFormatString = new char[MAX_FORMAT_STRING_LEN];      \ | ||||||
|  |          if (pFormatString) {                                        \ | ||||||
|  |             snprintf(pFormatString, MAX_FORMAT_STRING_LEN, X, Y);    \ | ||||||
|  |             fprintf(stderr, "%s -%s", appName, pFormatString);       \ | ||||||
|  |             delete[] pFormatString;                                  \ | ||||||
|  |          }                                                           \ | ||||||
|  |       }                                                              \ | ||||||
|  |    }                                                                 \ | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // | ||||||
|  | // Interlocked Increment and Decrement macros | ||||||
|  | //  | ||||||
|  | // Well, kind of interlocked :-). | ||||||
|  | //  | ||||||
|  | __inline static unsigned long | ||||||
|  | InterlockedIncrement(unsigned long *pValue) | ||||||
|  | { | ||||||
|  |    unsigned long retVal; | ||||||
|  |    pthread_mutex_lock(&interlockedMutex); | ||||||
|  |    (*pValue) ++; | ||||||
|  |    retVal = *pValue; | ||||||
|  |    pthread_mutex_unlock(&interlockedMutex); | ||||||
|  |    return retVal; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | __inline static unsigned long | ||||||
|  | InterlockedDecrement(unsigned long *pValue) | ||||||
|  | { | ||||||
|  |    unsigned long retVal; | ||||||
|  |    pthread_mutex_lock(&interlockedMutex); | ||||||
|  |    (*pValue) --; | ||||||
|  |    retVal = *pValue; | ||||||
|  |    pthread_mutex_unlock(&interlockedMutex); | ||||||
|  |    return retVal; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | __inline static uint32_t | ||||||
|  | InterlockedIncrement(uint32_t *pValue) | ||||||
|  | { | ||||||
|  |    uint32_t retVal; | ||||||
|  |    pthread_mutex_lock(&interlockedMutex); | ||||||
|  |    (*pValue) ++; | ||||||
|  |    retVal = *pValue; | ||||||
|  |    pthread_mutex_unlock(&interlockedMutex); | ||||||
|  |    return retVal; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | __inline static uint32_t | ||||||
|  | InterlockedDecrement(uint32_t *pValue) | ||||||
|  | { | ||||||
|  |    uint32_t retVal; | ||||||
|  |    pthread_mutex_lock(&interlockedMutex); | ||||||
|  |    (*pValue) --; | ||||||
|  |    retVal = *pValue; | ||||||
|  |    pthread_mutex_unlock(&interlockedMutex); | ||||||
|  |    return retVal; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | //===[ Include files ]===================================================== | ||||||
|  |  | ||||||
|  | //===[ External prototypes ]=============================================== | ||||||
|  |  | ||||||
|  | //===[ Manifest constants ]================================================ | ||||||
|  |  | ||||||
|  | //===[ Type definitions ]================================================== | ||||||
|  |  | ||||||
|  | //===[ Function prototypes ]=============================================== | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #endif // _IPCINT_ | ||||||
|  |  | ||||||
|  | //========================================================================= | ||||||
|  | //========================================================================= | ||||||
| @@ -0,0 +1,639 @@ | |||||||
|  | /*********************************************************************** | ||||||
|  |  *  | ||||||
|  |  *  Copyright (C) 2006 Novell, Inc. All Rights Reserved. | ||||||
|  |  * | ||||||
|  |  *  This library is free software; you can redistribute it and/or | ||||||
|  |  *  modify it under the terms of the GNU Lesser General Public | ||||||
|  |  *  License as published by the Free Software Foundation; version 2.1 | ||||||
|  |  *  of the License. | ||||||
|  |  * | ||||||
|  |  *  This library 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 | ||||||
|  |  *  Library Lesser General Public License for more details. | ||||||
|  |  * | ||||||
|  |  *  You should have received a copy of the GNU Lesser General Public | ||||||
|  |  *  License along with this library; if not, Novell, Inc. | ||||||
|  |  *  | ||||||
|  |  *  To contact Novell about this file by physical or electronic mail,  | ||||||
|  |  *  you may find current contact information at www.novell.com. | ||||||
|  |  *  | ||||||
|  |  *  Author: Juan Carlos Luciani <jluciani@novell.com> | ||||||
|  |  * | ||||||
|  |  ***********************************************************************/ | ||||||
|  |  | ||||||
|  |  | ||||||
|  | //===[ Include files ]===================================================== | ||||||
|  |  | ||||||
|  | #include "internal.h" | ||||||
|  |  | ||||||
|  | //===[ External data ]===================================================== | ||||||
|  |  | ||||||
|  | //===[ External prototypes ]=============================================== | ||||||
|  |  | ||||||
|  | //===[ Manifest constants ]================================================ | ||||||
|  |  | ||||||
|  | #define MAXFD 64 | ||||||
|  |  | ||||||
|  | #define DOMAIN_SOCKET_FILE_NAME "/var/CASA/authtoken/validate/socket" | ||||||
|  |  | ||||||
|  | //===[ Type definitions ]================================================== | ||||||
|  |  | ||||||
|  | //===[ Function prototypes ]=============================================== | ||||||
|  |  | ||||||
|  | void* | ||||||
|  | WorkerThread(void*); | ||||||
|  |  | ||||||
|  | //===[ Global variables ]================================================== | ||||||
|  |  | ||||||
|  | // Usage string | ||||||
|  | char  usage[] = "\nCasaAuthtokenValidateD: usage: [-p ListenPort] [-b BeginThreads] [-g GrowThreads] [-m MaxThreads] [-D DebugLevel] [-d]\n"; | ||||||
|  |  | ||||||
|  | // Worker thread pool configuration parameters | ||||||
|  | int   beginThreads = 5; | ||||||
|  | int   growThreads = 5; | ||||||
|  | int   maxThreads = 4096; | ||||||
|  | int   minWaitingThreads = beginThreads; | ||||||
|  |  | ||||||
|  | // Worker thread pool operating parameters | ||||||
|  | double   numThreads = 0; | ||||||
|  | double   numBusyThreads = 0; | ||||||
|  |  | ||||||
|  | // Listen Port Number | ||||||
|  | int   listenPortNumber = 5000; | ||||||
|  | //int   listenPortNumber = 0; | ||||||
|  |  | ||||||
|  | // Parameter indicating whether or not XSrv needs to run | ||||||
|  | // as a daemon. | ||||||
|  | bool  daemonize = false; | ||||||
|  |  | ||||||
|  | // Name to use for logging purposes | ||||||
|  | char  appName[] = "CasaAuthtokenValidateD"; | ||||||
|  |  | ||||||
|  | // Debug Level | ||||||
|  | int   DebugLevel = 3; | ||||||
|  | bool  UseSyslog = false; | ||||||
|  |  | ||||||
|  | // Variables for daemon auto-restart after crash feature | ||||||
|  | static bool autoRestartAfterCrash = true; | ||||||
|  |  | ||||||
|  | // Synchronization variables | ||||||
|  | pthread_mutex_t   interlockedMutex; | ||||||
|  | pthread_mutex_t   serverMutex; | ||||||
|  | pthread_cond_t    serverCondition; | ||||||
|  |  | ||||||
|  | // Operating parameters | ||||||
|  | bool  terminating = false; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | //++======================================================================= | ||||||
|  | void | ||||||
|  | GrowWorkerThreadPool(int growNumber) | ||||||
|  | // | ||||||
|  | //  Arguments:  | ||||||
|  | // | ||||||
|  | //  Returns:    | ||||||
|  | // | ||||||
|  | //  Abstract:   | ||||||
|  | // | ||||||
|  | //  Notes: The serverMutex needs to be held when calling this | ||||||
|  | //         procedure. | ||||||
|  | // | ||||||
|  | // L0 | ||||||
|  | //=======================================================================-- | ||||||
|  | { | ||||||
|  |    DbgTrace(1, "GrowWorkerThreadPool- Start\n", 0); | ||||||
|  |  | ||||||
|  |    for (int i = 0; i < growNumber; i++) | ||||||
|  |    { | ||||||
|  |       int threadCreateStatus; | ||||||
|  |       pthread_t thread; | ||||||
|  |  | ||||||
|  |       if ((threadCreateStatus = pthread_create(&thread, | ||||||
|  |                                                NULL, | ||||||
|  |                                                (void*(*)(void*))WorkerThread, | ||||||
|  |                                                NULL) == 0)) | ||||||
|  |       { | ||||||
|  |          // Worker thread created | ||||||
|  |          numThreads ++; | ||||||
|  |       } | ||||||
|  |       else | ||||||
|  |       { | ||||||
|  |          DbgTrace(0, "GrowWorkerThreadPool- Thread creation failed, status = %0d\n", threadCreateStatus); | ||||||
|  |       } | ||||||
|  |    } | ||||||
|  |  | ||||||
|  |    // Let our server know if we ended up with no worker threads | ||||||
|  |    if (numThreads == 0) | ||||||
|  |       pthread_cond_signal(&serverCondition); | ||||||
|  |  | ||||||
|  |    DbgTrace(1, "GrowWorkerThreadPool- End\n", 0); | ||||||
|  |  | ||||||
|  | }  /*-- GrowWorkerThreadPool() --*/ | ||||||
|  |  | ||||||
|  |  | ||||||
|  | //++======================================================================= | ||||||
|  | void | ||||||
|  | WorkerThreadBusy(void) | ||||||
|  | // | ||||||
|  | //  Arguments:  | ||||||
|  | // | ||||||
|  | //  Returns:    | ||||||
|  | // | ||||||
|  | //  Abstract:   | ||||||
|  | // | ||||||
|  | //  Notes: | ||||||
|  | // | ||||||
|  | // L0 | ||||||
|  | //=======================================================================-- | ||||||
|  | { | ||||||
|  |    DbgTrace(1, "WorkerThreadBusy- Start\n", 0); | ||||||
|  |  | ||||||
|  |    // Acquire our mutex | ||||||
|  |    pthread_mutex_lock(&serverMutex); | ||||||
|  |  | ||||||
|  |    // Increment the numBusyThread count and grow the number of worker threads | ||||||
|  |    // if necessary. | ||||||
|  |    numBusyThreads ++; | ||||||
|  |    if ((numThreads - numBusyThreads) < minWaitingThreads) | ||||||
|  |       GrowWorkerThreadPool(growThreads); | ||||||
|  |  | ||||||
|  |    // Release our mutex | ||||||
|  |    pthread_mutex_unlock(&serverMutex); | ||||||
|  |  | ||||||
|  |    DbgTrace(1, "WorkerThreadBusy- End\n", 0); | ||||||
|  |  | ||||||
|  | }  /*-- WorkerThreadBusy() --*/ | ||||||
|  |  | ||||||
|  |  | ||||||
|  | //++======================================================================= | ||||||
|  | bool | ||||||
|  | WorkerThreadWaiting(void) | ||||||
|  | // | ||||||
|  | //  Arguments:  | ||||||
|  | // | ||||||
|  | //  Returns:    | ||||||
|  | // | ||||||
|  | //  Abstract:   | ||||||
|  | // | ||||||
|  | //  Notes: | ||||||
|  | // | ||||||
|  | // L0 | ||||||
|  | //=======================================================================-- | ||||||
|  | { | ||||||
|  |    bool  retValue; | ||||||
|  |  | ||||||
|  |    DbgTrace(1, "WorkerThreadWaiting- Start\n", 0); | ||||||
|  |  | ||||||
|  |    // Acquire our mutex | ||||||
|  |    pthread_mutex_lock(&serverMutex); | ||||||
|  |  | ||||||
|  |    // Decrement the numBusyThread count and determine if there are | ||||||
|  |    // too many of us laying around. | ||||||
|  |    numBusyThreads --; | ||||||
|  |    if ((numThreads - numBusyThreads) > minWaitingThreads | ||||||
|  |        && ((numBusyThreads + growThreads) / numThreads) < 0.33 ) | ||||||
|  |       retValue = true; | ||||||
|  |    else | ||||||
|  |       retValue = false; | ||||||
|  |  | ||||||
|  |    // Release our mutex | ||||||
|  |    pthread_mutex_unlock(&serverMutex); | ||||||
|  |  | ||||||
|  |    DbgTrace(1, "WorkerThreadWaiting- End, retValue = %X\n", retValue); | ||||||
|  |  | ||||||
|  |    return retValue; | ||||||
|  |  | ||||||
|  | }  /*-- WorkerThreadWaiting() --*/ | ||||||
|  |  | ||||||
|  |  | ||||||
|  | //++======================================================================= | ||||||
|  | void* | ||||||
|  | WorkerThread(void*) | ||||||
|  | // | ||||||
|  | //  Arguments:  | ||||||
|  | // | ||||||
|  | //  Returns:    | ||||||
|  | // | ||||||
|  | //  Abstract:   | ||||||
|  | // | ||||||
|  | //  Notes: | ||||||
|  | // | ||||||
|  | // L0 | ||||||
|  | //=======================================================================-- | ||||||
|  | { | ||||||
|  |    DbgTrace(1, "WorkerThread- Start\n", 0); | ||||||
|  |  | ||||||
|  |    // Set the thread in the detached state so that it is cleaned up when it exits | ||||||
|  | 	pthread_detach(pthread_self()); | ||||||
|  |  | ||||||
|  |    // Loop until told to terminate | ||||||
|  |    while (!terminating) | ||||||
|  |    { | ||||||
|  |       // Get a request that needs servicing | ||||||
|  |       int32_t requestId = IpcServerGetRequest(); | ||||||
|  |       if (requestId != 0) | ||||||
|  |       { | ||||||
|  |          // We got a request that needs servicing, now get the | ||||||
|  |          // data associated with it. | ||||||
|  |          char *pReqData; | ||||||
|  |          int dataLen = IpcServerGetRequestData(requestId, &pReqData); | ||||||
|  |          if (dataLen != 0) | ||||||
|  |          { | ||||||
|  |             // Indicate that we are now busy | ||||||
|  |             WorkerThreadBusy(); | ||||||
|  |  | ||||||
|  |             // Just echo the data back as the reply | ||||||
|  |             IpcServerCompleteRequest(requestId, pReqData); | ||||||
|  |  | ||||||
|  |             // Indicate that we are no longer busy and get indication of | ||||||
|  |             // whether or not we should continue to try to process requests. | ||||||
|  |             if (WorkerThreadWaiting() == true) | ||||||
|  |             { | ||||||
|  |                DbgTrace(1, "WorkerThread- Requested to terminate\n", 0); | ||||||
|  |                break; | ||||||
|  |             } | ||||||
|  |          } | ||||||
|  |          else | ||||||
|  |          { | ||||||
|  |             DbgTrace(0, "WorkerThread- Error obtaining Request data\n", 0); | ||||||
|  |             IpcServerAbortRequest(requestId); | ||||||
|  |          } | ||||||
|  |       } | ||||||
|  |       else | ||||||
|  |       { | ||||||
|  |          // No need to service requests any longer | ||||||
|  |          break; | ||||||
|  |       } | ||||||
|  |    } | ||||||
|  |  | ||||||
|  |    // Decrement the number of worker threads and signal our main thread | ||||||
|  |    // to terminate itself if we are the last worker thread. | ||||||
|  |    pthread_mutex_lock(&serverMutex); | ||||||
|  |    numThreads --; | ||||||
|  |    if (numThreads == 0) | ||||||
|  |       pthread_cond_signal(&serverCondition); | ||||||
|  |    pthread_mutex_unlock(&serverMutex); | ||||||
|  |  | ||||||
|  |    DbgTrace(1, "WorkerThread- End\n", 0); | ||||||
|  |  | ||||||
|  |    // Exit | ||||||
|  | 	pthread_exit(NULL); | ||||||
|  |  | ||||||
|  | 	return 0;	// never-reached! | ||||||
|  |  | ||||||
|  | }  /*-- WorkerThread() --*/ | ||||||
|  |  | ||||||
|  |  | ||||||
|  | //++======================================================================= | ||||||
|  | void | ||||||
|  | SigTermHandler( | ||||||
|  |    int signum) | ||||||
|  | // | ||||||
|  | //  Arguments:  | ||||||
|  | // | ||||||
|  | //  Returns:    | ||||||
|  | // | ||||||
|  | //  Abstract:   | ||||||
|  | // | ||||||
|  | //  Notes: | ||||||
|  | // | ||||||
|  | // L2 | ||||||
|  | //=======================================================================-- | ||||||
|  | { | ||||||
|  |    DbgTrace(1, "SigTermHandler- Start\n", 0); | ||||||
|  |  | ||||||
|  |    // Indicate that we are terminating | ||||||
|  |    terminating = true; | ||||||
|  |  | ||||||
|  |    // Shutdown the IPC Server | ||||||
|  |    IpcServerShutdown(); | ||||||
|  |  | ||||||
|  |    DbgTrace(1, "SigTermHandler- End\n", 0); | ||||||
|  |  | ||||||
|  | }  /*-- SigTermHandler() --*/ | ||||||
|  |  | ||||||
|  |  | ||||||
|  | //++======================================================================= | ||||||
|  | void | ||||||
|  | DaemonInit( | ||||||
|  |    const char *pname) | ||||||
|  | // | ||||||
|  | //  Arguments:  | ||||||
|  | // | ||||||
|  | //  Returns:    | ||||||
|  | // | ||||||
|  | //  Abstract:   | ||||||
|  | // | ||||||
|  | //  Notes: Copy of daemon_init() in Richard Stevens Unix Network | ||||||
|  | //         Programming Book. | ||||||
|  | // | ||||||
|  | // L0 | ||||||
|  | //=======================================================================-- | ||||||
|  | { | ||||||
|  |    pid_t             pid; | ||||||
|  |    char              *pNoAutoRestartEnvvar; | ||||||
|  |  | ||||||
|  |    DbgTrace(1, "DaemonInit- Start\n", 0); | ||||||
|  |  | ||||||
|  |    // Determine if we need to disable the auto-restart after crash feature | ||||||
|  |    if ((pNoAutoRestartEnvvar = getenv("CASA_NO_AUTORESTART_AFTER_CRASH")) != NULL | ||||||
|  |        && strcmp(pNoAutoRestartEnvvar, "0") != 0) | ||||||
|  |    { | ||||||
|  |       DbgTrace(1, "DaemonInit- Disabling daemon auto-restart after crash feature\n", 0); | ||||||
|  |       autoRestartAfterCrash = false; | ||||||
|  |    } | ||||||
|  |  | ||||||
|  |    // Fork to run in the background, check for error. | ||||||
|  |    if ((pid = fork()) == -1) | ||||||
|  |    { | ||||||
|  |       DbgTrace(0, "DaemonInit- Fork error = %d\n", errno); | ||||||
|  |       exit(0); | ||||||
|  |    } | ||||||
|  |    else if (pid != 0) | ||||||
|  |    { | ||||||
|  |       // The fork succeeded and we are the parent process, terminate | ||||||
|  |       // ourselves. | ||||||
|  |       exit(0); | ||||||
|  |    } | ||||||
|  |  | ||||||
|  |    /* 1st child continues */ | ||||||
|  |  | ||||||
|  |    // Become the session leader and set to ignore SIGHUP | ||||||
|  |    setsid(); | ||||||
|  |    signal(SIGHUP, SIG_IGN); | ||||||
|  |  | ||||||
|  |    // Fork again to guarantee that the daemon can not acquire a | ||||||
|  |    // controlling terminal. | ||||||
|  |    if ((pid = fork()) == -1) | ||||||
|  |    { | ||||||
|  |       DbgTrace(0, "DaemonInit- Fork error = %d\n", errno); | ||||||
|  |       exit(0); | ||||||
|  |    } | ||||||
|  |    else if (pid != 0) | ||||||
|  |    { | ||||||
|  |       // The fork succeeded and we are the parent process, terminate | ||||||
|  |       // ourselves. | ||||||
|  |       exit(0); | ||||||
|  |    } | ||||||
|  |  | ||||||
|  |    /* 2nd child continues */ | ||||||
|  |  | ||||||
|  |    // Close any open descriptors | ||||||
|  |    for (int i = 0; i < MAXFD; i++) | ||||||
|  |       close(i); | ||||||
|  |  | ||||||
|  |  | ||||||
|  |    // Spawn a worker | ||||||
|  |    if ((pid = fork()) == -1) | ||||||
|  |    { | ||||||
|  |       DbgTrace(0, "DaemonInit- Fork error = %d\n", errno); | ||||||
|  |       exit(0); | ||||||
|  |    } | ||||||
|  |    else if (pid == 0) | ||||||
|  |    { | ||||||
|  |       // The fork succeeded and we are the worker, continue. | ||||||
|  |    } | ||||||
|  |    else | ||||||
|  |    { | ||||||
|  |       // We are the parent of the server, check if we must execute the auto-restart | ||||||
|  |       // server after crash logic. | ||||||
|  |       if (autoRestartAfterCrash) | ||||||
|  |       { | ||||||
|  |          // Execute auto-restart server after crash logic | ||||||
|  |          while (1) | ||||||
|  |          { | ||||||
|  |             int childExitStatus; | ||||||
|  |  | ||||||
|  |             // Wait for children to exit | ||||||
|  |             pid = wait(&childExitStatus); | ||||||
|  |             if (pid != -1) | ||||||
|  |             { | ||||||
|  |                // Fork worker | ||||||
|  |                if ((pid = fork()) == -1) | ||||||
|  |                { | ||||||
|  |                   DbgTrace(0, "DaemonInit- Fork error = %d\n", errno); | ||||||
|  |                   exit(0); | ||||||
|  |                } | ||||||
|  |                else if (pid == 0) | ||||||
|  |                { | ||||||
|  |                   // The fork succeeded and we are the server, exit the loop | ||||||
|  |                   // to start. | ||||||
|  |                   goto childContinue; | ||||||
|  |                } | ||||||
|  |  | ||||||
|  |                // We are the parent process, continue to watch for a terminated child process. | ||||||
|  |                syslog(LOG_USER | LOG_INFO, "CasaAuthtokenValidateD: Worker re-started after it terminated unexpectedly"); | ||||||
|  |                sleep(1);   // To keep from consuming too many cycles | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                // Check if we must exit the loop | ||||||
|  |                if (errno != EINTR) | ||||||
|  |                   break; | ||||||
|  |             } | ||||||
|  |          } | ||||||
|  |       } | ||||||
|  |        | ||||||
|  |       // Terminate ourselves. | ||||||
|  |       exit(0); | ||||||
|  |    } | ||||||
|  |  | ||||||
|  | childContinue: | ||||||
|  |  | ||||||
|  |    // Set flag to inform DbgTrace macros to use Syslog | ||||||
|  |    UseSyslog = true; | ||||||
|  |  | ||||||
|  |    // Change the working directory | ||||||
|  |    chdir("/var/CASA/authtoken/validate"); | ||||||
|  |  | ||||||
|  |    // Clear our file mode creation mask | ||||||
|  |    umask(0); | ||||||
|  |  | ||||||
|  |    // Get ready to log | ||||||
|  |    openlog(appName, LOG_CONS | LOG_NOWAIT | LOG_ODELAY| LOG_PID, LOG_USER); | ||||||
|  |  | ||||||
|  |    if (DebugLevel == 0) | ||||||
|  |       setlogmask(LOG_UPTO(LOG_INFO)); | ||||||
|  |    else | ||||||
|  |       setlogmask(LOG_UPTO(LOG_DEBUG)); | ||||||
|  |  | ||||||
|  |    DbgTrace(1, "DaemonInit- End\n", 0); | ||||||
|  |  | ||||||
|  | }  /*-- DaemonInit() --*/ | ||||||
|  |  | ||||||
|  |  | ||||||
|  | //++======================================================================= | ||||||
|  | int | ||||||
|  | main( | ||||||
|  |    int argc, | ||||||
|  |    char* argv[]) | ||||||
|  | // | ||||||
|  | //  Arguments:  | ||||||
|  | // | ||||||
|  | //  Returns:    | ||||||
|  | // | ||||||
|  | //  Abstract:   | ||||||
|  | // | ||||||
|  | //  Notes: | ||||||
|  | // | ||||||
|  | // L0 | ||||||
|  | //=======================================================================-- | ||||||
|  | { | ||||||
|  |    int         optionsSpecified = 0; | ||||||
|  |    bool        doneScanning = false; | ||||||
|  |    bool        invalidOption = false; | ||||||
|  |    int         option; | ||||||
|  |  | ||||||
|  |    //printf("**** AuthTokenValidate Daemon ****\n"); | ||||||
|  |  | ||||||
|  |    // Scan through the options specified | ||||||
|  |    while (!doneScanning) | ||||||
|  |    { | ||||||
|  |       opterr = 0; | ||||||
|  |       option = getopt(argc, argv, "m:p:b:g:D:d"); | ||||||
|  |  | ||||||
|  |       // Proceed based on the result | ||||||
|  |       switch (option) | ||||||
|  |       { | ||||||
|  |          case 'p': | ||||||
|  |             // Port number option, record location of | ||||||
|  |             // argument. | ||||||
|  |             listenPortNumber = atoi(optarg); | ||||||
|  |  | ||||||
|  |             optionsSpecified ++; | ||||||
|  |             break; | ||||||
|  |  | ||||||
|  |          case 'b': | ||||||
|  |             // Begin threads option, override the default parameter | ||||||
|  |             // with the value of the option. | ||||||
|  |             beginThreads = atoi(optarg); | ||||||
|  |  | ||||||
|  |             optionsSpecified ++; | ||||||
|  |             break; | ||||||
|  |  | ||||||
|  |          case 'g': | ||||||
|  |             // Grow threads option, override the default parameter | ||||||
|  |             // with the value of the option. | ||||||
|  |             growThreads = atoi(optarg); | ||||||
|  |  | ||||||
|  |             optionsSpecified ++; | ||||||
|  |             break; | ||||||
|  |  | ||||||
|  |          case 'm': | ||||||
|  |             // Max threads option, override the default parameter | ||||||
|  |             // with the value of the option. | ||||||
|  |             maxThreads = atoi(optarg); | ||||||
|  |  | ||||||
|  |             optionsSpecified ++; | ||||||
|  |             break; | ||||||
|  |  | ||||||
|  |          case 'd': | ||||||
|  |             // Run as daemon option | ||||||
|  |             daemonize = true; | ||||||
|  |     | ||||||
|  |             optionsSpecified ++; | ||||||
|  |             break; | ||||||
|  |  | ||||||
|  |          case 'D': | ||||||
|  |             // Set the debug level | ||||||
|  |             DebugLevel = atoi(optarg); | ||||||
|  |             optionsSpecified++; | ||||||
|  |             break; | ||||||
|  |  | ||||||
|  |          case '?': | ||||||
|  |             // Invalid option detected | ||||||
|  |             doneScanning = true; | ||||||
|  |             invalidOption = true; | ||||||
|  |             break; | ||||||
|  |  | ||||||
|  |          default: | ||||||
|  |             // Done scanning | ||||||
|  |             doneScanning = true; | ||||||
|  |             break; | ||||||
|  |       } | ||||||
|  |    } | ||||||
|  |  | ||||||
|  |    // Do some sanity checking | ||||||
|  |    if (!invalidOption | ||||||
|  |        && beginThreads > 0 | ||||||
|  |        && maxThreads > (growThreads+beginThreads) | ||||||
|  |        && beginThreads <= maxThreads) | ||||||
|  |    { | ||||||
|  |       // The server is ready to start, check if we must | ||||||
|  |       // run it as a daemon. | ||||||
|  |       if (daemonize) | ||||||
|  |          DaemonInit(argv[0]); | ||||||
|  |  | ||||||
|  |       // Set a handler for SIGTERM | ||||||
|  |       signal(SIGTERM, SigTermHandler); | ||||||
|  |  | ||||||
|  |       // Initialize our mutexes | ||||||
|  |       pthread_mutex_init(&interlockedMutex, NULL); | ||||||
|  |       pthread_mutex_init(&serverMutex, NULL); | ||||||
|  |  | ||||||
|  |       // Initialize the condition that we will use to wait | ||||||
|  |       // for the exit of all of our worker threads. | ||||||
|  |       if (pthread_cond_init(&serverCondition, NULL) == 0) | ||||||
|  |       { | ||||||
|  |          // Initialize the IPC Server | ||||||
|  |          if (IpcServerInit(appName, | ||||||
|  |                            DebugLevel, | ||||||
|  |                            UseSyslog) == 0) | ||||||
|  |          { | ||||||
|  |             // Now setup the appropriate listen address | ||||||
|  |             int setAddressResult; | ||||||
|  |             if (listenPortNumber == 0) | ||||||
|  |                setAddressResult = IpcServerSetUnAddress(DOMAIN_SOCKET_FILE_NAME); | ||||||
|  |             else | ||||||
|  |                setAddressResult = IpcServerSetInAddress(listenPortNumber); | ||||||
|  |  | ||||||
|  |             if (setAddressResult == 0) | ||||||
|  |             { | ||||||
|  |                // Now start the IPC server | ||||||
|  |                if (IpcServerStart() == 0) | ||||||
|  |                { | ||||||
|  |                   // Acquire our mutex | ||||||
|  |                   pthread_mutex_lock(&serverMutex); | ||||||
|  |  | ||||||
|  |                   // Start worker threads | ||||||
|  |                   GrowWorkerThreadPool(beginThreads); | ||||||
|  |  | ||||||
|  |                   // Wait for the worker threads to terminate | ||||||
|  |                   pthread_cond_wait(&serverCondition, &serverMutex); | ||||||
|  |  | ||||||
|  |                   // Release our mutex | ||||||
|  |                   pthread_mutex_unlock(&serverMutex); | ||||||
|  |                } | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                DbgTrace(0, "main- Setting of listen address failed\n", 0); | ||||||
|  |             } | ||||||
|  |          } | ||||||
|  |          else | ||||||
|  |          { | ||||||
|  |             DbgTrace(0, "main- Initialization of Ipc server failed\n", 0); | ||||||
|  |          } | ||||||
|  |       } | ||||||
|  |       else | ||||||
|  |       { | ||||||
|  |          DbgTrace(0, "main- Condition initialization failed\n", 0); | ||||||
|  |       } | ||||||
|  |    } | ||||||
|  |    else | ||||||
|  |    { | ||||||
|  |       // Invalid option detected or the user failed to | ||||||
|  |       // specify the listening port number. | ||||||
|  |       printf(usage, argv[0]); | ||||||
|  |    } | ||||||
|  |  | ||||||
|  |    return 0; | ||||||
|  |  | ||||||
|  | }  /*-- main() --*/ | ||||||
|  |  | ||||||
|  |  | ||||||
|  | //========================================================================= | ||||||
|  | //========================================================================= | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -63,7 +63,7 @@ RESOURCES = | |||||||
| DEFINES += -Wno-format-extra-args -fno-strict-aliasing -fshort-wchar  | DEFINES += -Wno-format-extra-args -fno-strict-aliasing -fshort-wchar  | ||||||
| CFLAGS += $(INCLUDES) $(DEFINES) | CFLAGS += $(INCLUDES) $(DEFINES) | ||||||
| CPPFLAGS += -fPIC $(INCLUDES) $(DEFINES) | CPPFLAGS += -fPIC $(INCLUDES) $(DEFINES) | ||||||
| LIBS = -lpthread -ldl -lexpat | LIBS = -lpthread | ||||||
| LDFLAGS = -Bsymbolic -shared -Wl,-soname=$(MODULE_NAME).$(MODULE_EXT) | LDFLAGS = -Bsymbolic -shared -Wl,-soname=$(MODULE_NAME).$(MODULE_EXT) | ||||||
|  |  | ||||||
| OBJDIR = ./$(TARGET_CFG)/$(LIB) | OBJDIR = ./$(TARGET_CFG)/$(LIB) | ||||||
|   | |||||||
| @@ -63,7 +63,7 @@ RESOURCES = | |||||||
| DEFINES += -Wno-format-extra-args -fno-strict-aliasing -fshort-wchar  | DEFINES += -Wno-format-extra-args -fno-strict-aliasing -fshort-wchar  | ||||||
| CFLAGS += $(INCLUDES) $(DEFINES) | CFLAGS += $(INCLUDES) $(DEFINES) | ||||||
| CPPFLAGS += -fPIC $(INCLUDES) $(DEFINES) | CPPFLAGS += -fPIC $(INCLUDES) $(DEFINES) | ||||||
| LIBS = -lpthread -ldl -lexpat | LIBS = -lpthread | ||||||
| LDFLAGS = -Bsymbolic -shared -Wl,-soname=$(MODULE_NAME).$(MODULE_EXT) | LDFLAGS = -Bsymbolic -shared -Wl,-soname=$(MODULE_NAME).$(MODULE_EXT) | ||||||
|  |  | ||||||
| OBJDIR = ./$(TARGET_CFG)/$(LIB) | OBJDIR = ./$(TARGET_CFG)/$(LIB) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user