#include <mach/mach.h>
#include <mach/mach_error.h>
#include <IOKit/pwr_mgt/IOPM.h>
#include <IOKit/pwr_mgt/IOPMLib.h>
#include <IOKit/pwr_mgt/IOPMLibDefs.h>
#include <IOKit/IOMessage.h>
#include <SystemConfiguration/SCDynamicStorePrivate.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/resource.h>
#include "SLPSystemConfiguration.h"
#include "mslp_sd.h"
#include "slp.h"
#include "mslp.h"
#include "mslpd_store.h"
#include "mslp_dat.h"
#include "mslplib.h"
#include "mslpd.h"
#include "slpipc.h"
#include "SLPRegistrar.h"
static int InitializeListeners( SAState* psa );
static int assign_defaults(SAState *psa, struct sockaddr_in *psin,
int argc, char *argv[]);
static void exit_handler(int signo);
#ifdef EXTRA_MSGS
static SLPInternalError fcopy(const char *pc1, const char *pc2);
#endif
static MslpdResources global_resources;
static bool _Terminated = false;
static int _Signal = 0;
bool IsProcessTerminated( void )
{
return _Terminated;
}
SLPBoolean AreWeADirectoryAgent( void )
{
SLPBoolean isDA = SLP_FALSE;
if ( SLPGetProperty("com.apple.slp.isDA") && !SDstrcasecmp(SLPGetProperty("com.apple.slp.isDA"),"true") )
isDA = SLP_TRUE;
return isDA;
}
SLPInternalError reset_slpd( int argc, char *pcArgv[], struct sockaddr_in *psin, SAState* psa )
{
const char * pcFile;
#ifdef ENABLE_SLP_LOGGING
SLP_LOG( SLP_LOG_STATE, "*** slpd reset ***" );
#endif
#ifdef EXTRA_MSGS
if ((psa->pvMutex = SDGetMutex(MSLP_SERVER)) == NULL)
{
LOG_SLP_ERROR_AND_RETURN(SLP_LOG_ERR,"slpd: could not init mutex",SLP_INTERNAL_SYSTEM_ERROR);
}
global_resources.pvMutex = psa->pvMutex;
SDLock(psa->pvMutex);
#endif
if ( argc > 4 || (argc > 1 && pcArgv[1][0] == '?') )
{
fprintf( stderr,
"Usage: %s [<filename>] [-f <config-file>]\n"
" <filename> is a serialized registration file. (optional)\n"
" <config-file> is a configuration file. (optional)\n",
pcArgv[0] );
return SLP_PARSE_ERROR;
}
if ( assign_defaults( psa, psin, argc, pcArgv ) != SLP_OK )
{
LOG_SLP_ERROR_AND_RETURN(SLP_LOG_ERR,"Could not assign defaults - abort",SLP_INTERNAL_SYSTEM_ERROR);
return SLP_INTERNAL_SYSTEM_ERROR;
}
InitializeSLPdSystemConfigurator();
DeleteRegFileIfFirstStartupSinceBoot();
if ((pcFile = SLPGetProperty("net.slp.serializedRegURL")) != NULL)
{
pcFile = &pcFile[strlen("file:")];
}
if (pcFile)
{
if (process_regfile(&(psa->store), pcFile) < 0)
{
SLP_LOG(SLP_LOG_ERR,"mslpd: could not process regfile",SLP_INTERNAL_SYSTEM_ERROR);
exit(-2);
}
}
#ifdef EXTRA_MSGS
#ifdef ENABLE_SLP_LOGGING
if ( SLPGetProperty("com.sun.slp.regfile") )
SLP_LOG(SLP_LOG_DEBUG, "reset_slpd, handling regfile: %s", SLPGetProperty("com.sun.slp.regfile"));
else
SLP_LOG(SLP_LOG_DEBUG, "reset_slpd, regfile property is NULL!");
#endif
if ( pcFile )
{
fcopy(pcFile,SLPGetProperty("com.sun.slp.regfile"));
}
else
{
FILE *fp = fopen(SLPGetProperty("com.sun.slp.regfile"),"r+");
if (!fp)
fp = fopen(SLPGetProperty("com.sun.slp.regfile"),"w+");
if (!fp)
{
SLP_LOG(SLP_LOG_ERR,"mslpd: could not initialize regfile %s",strerror(errno));
return SLP_PREFERENCES_ERROR;
}
else
{
if ( getc(fp) == EOF ) {
if (fprintf(fp,"# initial reg file automatically generated\n") < 0)
{
fclose(fp);
SLP_LOG(SLP_LOG_ERR,"mslpd: could not write to regfile %s",strerror(errno));
return SLP_PREFERENCES_ERROR;
}
if (SDchmod_writable(SLPGetProperty("com.sun.slp.regfile")) < 0)
{
fclose(fp);
SLP_LOG(SLP_LOG_ERR,"mslpd: could not change file permissions",strerror(errno));
return SLP_PREFERENCES_ERROR;
}
}
else if (process_regfile(&(psa->store), SLPGetProperty("com.sun.slp.regfile")) < 0) {
fclose(fp);
LOG_SLP_ERROR_AND_RETURN(SLP_LOG_ERR,"mslpd: could not process regfile",SLP_INTERNAL_SYSTEM_ERROR);
}
if (fclose(fp) < 0)
{
SLP_LOG(SLP_LOG_ERR,"mslpd: could not close reg file",strerror(errno));
return SLP_PREFERENCES_ERROR;
}
}
}
SDUnlock(psa->pvMutex);
#endif
#ifdef ENABLE_SLP_LOGGING
SLP_LOG( SLP_LOG_DEBUG, "reset_slpd finished" );
#endif
return SLP_OK;
}
int InitializeListeners( SAState* psa )
{
int err = 0;
err = InitializeInternalProcessListener( psa );
if ( !err )
err = InitializeTCPListener( psa );
if ( !err )
err = InitializeUDPListener( psa );
return err;
}
enum
{
kSignalMessage = 1000
};
typedef struct SignalMessage
{
mach_msg_header_t header;
mach_msg_body_t body;
int signum;
mach_msg_trailer_t trailer;
} SignalMessage;
static void SignalHandler(int signum);
static void SignalMessageHandler(CFMachPortRef port,SignalMessage *msg,CFIndex size,void *info);
static mach_port_t gSignalPort = MACH_PORT_NULL;
void SignalHandler(int signum)
{
SignalMessage msg;
msg.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND,0);
msg.header.msgh_size = sizeof(msg) - sizeof(mach_msg_trailer_t);
msg.header.msgh_remote_port = gSignalPort;
msg.header.msgh_local_port = MACH_PORT_NULL;
msg.header.msgh_id = kSignalMessage;
msg.body.msgh_descriptor_count = 0;
msg.signum = signum;
mach_msg(&msg.header,(MACH_SEND_MSG | MACH_SEND_TIMEOUT),
msg.header.msgh_size,0,MACH_PORT_NULL,0,MACH_PORT_NULL);
}
void SignalMessageHandler(CFMachPortRef port,SignalMessage *msg,CFIndex size,void *info)
{
CFRunLoopStop(CFRunLoopGetCurrent());
}
int main(int argc, char *pcArgv[])
{
SAState sa;
struct sockaddr_in sin;
int err = 0;
const char * pcScopes;
struct rlimit rlim;
rlim_t i;
if ( argc > 1 && strcmp(pcArgv[1], "-v") == 0 )
{
fprintf( stdout, "%s\n", SLPD_VERSION );
exit(0);
}
#ifdef ENABLE_SLP_LOGGING
SLP_LOG( SLP_LOG_STATE, "*** slpd started ***" );
#endif
if ( getrlimit(RLIMIT_NOFILE, &rlim) < 0 )
{
SLPLOG(SLP_LOG_ERR, "Failed to getrlimit!");
rlim.rlim_cur = FD_SETSIZE;
}
for ( i=rlim.rlim_cur; i>=0; i-- )
close(i);
struct sigaction sSigAction, sSigOldAction ;
sigemptyset (&sSigAction.sa_mask) ;
sSigAction.sa_handler = (void (*) (int)) SIG_IGN ;
::sigaction (SIGPIPE, &sSigAction, &sSigOldAction) ;
mach_port_limits_t limits = { 1 };
CFMachPortRef port;
port = CFMachPortCreate(NULL,(CFMachPortCallBack)SignalMessageHandler,NULL,NULL);
CFRunLoopAddSource(CFRunLoopGetCurrent(),CFMachPortCreateRunLoopSource(NULL,port,0),kCFRunLoopCommonModes);
gSignalPort = CFMachPortGetPort(port);
mach_port_set_attributes(mach_task_self(),gSignalPort,MACH_PORT_LIMITS_INFO,
(mach_port_info_t)&limits,sizeof(limits) / sizeof(natural_t));
signal(SIGINT,SignalHandler);
if ( IsNetworkSetToTriggerDialup() )
{
SLPLOG(SLP_LOG_ERR, "slpd: Network is set to auto dial ppp, - abort");
return 0;
}
OPEN_NETWORKING(); InitSLPRegistrar();
memset( &sa, 0, sizeof(SAState) );
err = reset_slpd( argc, pcArgv, &sin, &sa );
if ( err )
{
SLPLOG(SLP_LOG_ERR, "slpd: could not do initial setup - abort");
return err;
}
SDatexit(exit_handler);
if (mslpd_init_network(&sa) != SLP_OK)
{
SLPLOG(SLP_LOG_FAIL, "slpd: could not init networking - abort");
return -3;
}
global_resources.sdUDP = sa.sdUDP;
global_resources.sdTCP = sa.sdTCP;
if ((pcScopes = SLPGetProperty("net.slp.useScopes"))==NULL)
{
pcScopes = SLP_DEFAULT_SCOPE;
}
InitializeListeners( &sa );
StartSLPUDPListener( &sa );
StartSLPTCPListener( &sa );
if ( AreWeADirectoryAgent() )
{
StartSLPDAAdvertiser( &sa );
SLPRegistrar::TheSLPR()->EnableRAdminNotification();
}
InitializeSLPDARegisterer( &sa );
StartSLPDALocator( (void *)mslpd_daadvert_callback, CFRunLoopGetCurrent(), &sa );
sa.pdat = GetGlobalDATable();
#ifdef ENABLE_SLP_LOGGING
SLP_LOG( SLP_LOG_MSG, "slpd: initialization finished");
#endif
err = RunSLPInternalProcessListener( &sa );
CFRunLoopRun();
#ifdef ENABLE_SLP_LOGGING
SLP_LOG( SLP_LOG_MSG, "slpd: exiting");
#endif
return _Signal;
}
static int assign_defaults(SAState *psa, struct sockaddr_in *psin,
int argc, char *argv[])
{
char* daScopeListTemp = NULL;
SLPBoolean needToSetOverflow = SLP_FALSE;
psa->tvTimeout.tv_sec = 1L;
psa->tvTimeout.tv_usec = 0L;
memset(psin,0,sizeof(struct sockaddr_in));
SLPSetProperty("com.sun.slp.isSA","true");
#ifdef MAC_OS_X
SLPSetProperty("com.apple.slp.isDA", "false");
#endif
SLPSetProperty("net.slp.useScopes",SLP_DEFAULT_SCOPE); SLPSetProperty("com.apple.slp.defaultRegistrationScope",SLP_DEFAULT_SCOPE); SLPSetProperty("com.apple.slp.daScopeList", SLP_DEFAULT_SCOPE); SLPSetProperty("net.slp.multicastTTL",MCAST_TTL);
SLPSetProperty("net.slp.MTU",SENDMTU);
if (!SLPGetProperty("com.apple.slp.port"))
SLPSetProperty("com.apple.slp.port","427");
SLPSetProperty("net.slp.locale","en");
SLPSetProperty("com.sun.slp.minRefreshInterval","10800");
SLPSetProperty("com.apple.slp.logfile",LOG_FILE);
SLPSetProperty("com.apple.slp.identity", "slpd");
if (argc == 4 && !strcmp(argv[2], "-f"))
{
char buf[1029];
SLPReadConfigFile(argv[3]);
sprintf(buf,"file:%s",argv[1]);
SLPSetProperty("net.slp.serializedRegURL",buf);
}
else if (argc == 3 && !strcmp(argv[1],"-f"))
{
SLPReadConfigFile(argv[2]);
}
if ( SLPGetProperty( "com.apple.slp.daScopeList" ) && strlen(SLPGetProperty( "com.apple.slp.daScopeList" ) ) > kMaxScopeListLenForUDP )
{
daScopeListTemp = (char*)malloc( strlen(SLPGetProperty( "com.apple.slp.daScopeList" ) ) +1 );
strcpy( daScopeListTemp, SLPGetProperty( "com.apple.slp.daScopeList" ) );
while ( strlen( daScopeListTemp ) > kMaxScopeListLenForUDP ) {
char* tempPtr = strrchr( daScopeListTemp, ',' );
needToSetOverflow = SLP_TRUE;
if ( !tempPtr )
{
free( daScopeListTemp );
SLP_LOG( SLP_LOG_FAIL, "we can't fit a single scope in our advertisement!" ); break;
}
if ( tempPtr && *tempPtr == ',' )
*tempPtr = '\0'; }
}
if ( needToSetOverflow == SLP_TRUE )
{
SLPSetProperty( "com.apple.slp.daPrunedScopeList", daScopeListTemp );
#ifdef ENABLE_SLP_LOGGING
SLP_LOG( SLP_LOG_RADMIN, "We have a scope list that is longer than can be advertised via multicasting. Some SLP implementations may see a truncated list." );
#endif
}
else
SLPSetProperty( "com.apple.slp.daPrunedScopeList", NULL );
if ( daScopeListTemp )
free( daScopeListTemp );
#ifndef NDEBUG
#ifdef ENABLE_SLP_LOGGING
if ( AreWeADirectoryAgent() )
mslplog(SLP_LOG_DEBUG,"slpd as a DA started with scopes", SLPGetProperty("com.apple.slp.daScopeList"));
else
mslplog(SLP_LOG_DEBUG,"slpd as an SA started with scopes", SLPGetProperty("net.slp.useScopes"));
#endif
#endif
psin->sin_family = AF_INET;
psin->sin_port = htons(SLP_PORT);
if (SLPGetProperty("net.slp.isBroadcastOnly") != NULL &&
!(SDstrcasecmp(SLPGetProperty("net.slp.isBroadcastOnly"),"true")))
{
psin->sin_addr.s_addr = BROADCAST;
}
else
{
psin->sin_addr.s_addr = SLP_MCAST;
}
psa->pdat = GetGlobalDATable();
#ifdef EXTRA_MSGS
if (!SLPGetProperty("com.sun.slp.regfile"))
SLPSetProperty("com.sun.slp.regfile",SDDefaultRegfile());
if (!SLPGetProperty("com.sun.slp.tempfile"))
SLPSetProperty("com.sun.slp.tempfile",SDDefaultTempfile());
#endif
return SLP_OK;
}
void propogate_all_advertisements(SAState *psa)
{
int i, needToCheckDAList=0;
DATable* pdat = GetGlobalDATable();
SLPInternalError err = SLP_OK;
if ( !psa || !pdat )
return;
LockGlobalDATable();
for (i = 0; i < pdat->iSize; i++)
{
if ( pdat->pDAE[i].iStrikes <= kNumberOfStrikesAllowed )
{
err = propogate_registrations(psa,pdat->pDAE[i].sin, pdat->pDAE[i].pcScopeList);
if ( err )
{
#ifdef ENABLE_SLP_LOGGING
SLP_LOG( SLP_LOG_DA, "Error trying to propogate a registration to DA: %s", inet_ntoa(pdat->pDAE[i].sin.sin_addr) ); #endif
pdat->pDAE[i].iStrikes++;
needToCheckDAList = 1; }
}
}
UnlockGlobalDATable();
if ( needToCheckDAList )
dat_boot_off_struck_out_das();
}
void propogate_registration( SAState *psa, const char* lang, const char* srvtype, const char* url, const char* scopeList, const char* attrlist, int life )
{
int i, needToCheckDAList=0;
SLPInternalError err = SLP_OK;
DATable* pdat = GetGlobalDATable();
const char* pcSL = GetEncodedScopeToRegisterIn();
if ( !psa || !pdat )
return;
if ( scopeList && scopeList[0] != '\0' )
pcSL = scopeList;
LockGlobalDATable();
for (i = 0; i < pdat->iSize; i++)
{
if ( list_intersection(pdat->pDAE[i].pcScopeList, pcSL ) )
{
if ( pdat->pDAE[i].iStrikes <= kNumberOfStrikesAllowed )
{
err = propogate_registration_with_DA(psa,pdat->pDAE[i].sin, lang, url, srvtype, pcSL, attrlist, life);
if ( err )
{
#ifdef ENABLE_SLP_LOGGING
SLP_LOG( SLP_LOG_DA, "Error trying to propogate a registration to DA: %s", inet_ntoa(pdat->pDAE[i].sin.sin_addr) ); #endif
pdat->pDAE[i].iStrikes++;
needToCheckDAList = 1; }
}
}
#ifdef ENABLE_SLP_LOGGING
else
{
SLP_LOG( SLP_LOG_DEBUG, "Skipping registration propigation with DA: %s, DA ScopeList: %s, service ScopeList: %s", inet_ntoa(pdat->pDAE[i].sin.sin_addr), pdat->pDAE[i].pcScopeList, pcSL );
}
if ( err )
{
SLP_LOG( SLP_LOG_DA, "Error trying to propogate a registration to DA: %s", inet_ntoa(pdat->pDAE[i].sin.sin_addr) ); }
#endif
}
UnlockGlobalDATable();
if ( needToCheckDAList )
dat_boot_off_struck_out_das();
}
void propogate_deregistration( SAState *psa, const char* lang, const char* srvtype, const char* url, const char* scopeList, const char* attrlist, int life )
{
int i, needToCheckDAList=0;
SLPInternalError err = SLP_OK;
DATable* pdat = GetGlobalDATable();
const char* pcSL = GetEncodedScopeToRegisterIn();
if ( !psa || !pdat )
return;
if ( scopeList && scopeList[0] != '\0' )
pcSL = scopeList;
LockGlobalDATable();
for (i = 0; i < pdat->iSize; i++)
{
if ( list_intersection(pdat->pDAE[i].pcScopeList, pcSL ) )
{
err = propogate_deregistration_with_DA(psa,pdat->pDAE[i].sin, lang, url, srvtype, pcSL, attrlist, life);
if ( err )
{
#ifdef ENABLE_SLP_LOGGING
SLP_LOG( SLP_LOG_DA, "Error trying to propogate a registration to DA: %s", inet_ntoa(pdat->pDAE[i].sin.sin_addr) ); #endif
pdat->pDAE[i].iStrikes++;
needToCheckDAList = 1; }
}
#ifdef ENABLE_SLP_LOGGING
else
{
SLP_LOG( SLP_LOG_DEBUG, "Skipping deregistration propigation with DA: %s, DA ScopeList: %s, service ScopeList: %s", inet_ntoa(pdat->pDAE[i].sin.sin_addr), pdat->pDAE[i].pcScopeList, pcSL );
}
if ( err )
{
SLP_LOG( SLP_LOG_DA, "Error trying to propogate a deregistration to DA: %s", inet_ntoa(pdat->pDAE[i].sin.sin_addr) ); }
#endif
}
UnlockGlobalDATable();
if ( needToCheckDAList )
dat_boot_off_struck_out_das();
}
#ifdef EXTRA_MSGS
static SLPInternalError fcopy(const char *pc1, const char *pc2)
{
struct stat st1;
char buf[100];
FILE *fpSrc = NULL, *fpDest = NULL;
int total;
if (stat(pc1, &st1) < 0 ||
(fpSrc = fopen(pc1,"rb")) == NULL ||
(fpDest = fopen(pc2,"wb")) == NULL)
{
if (fpSrc)
fclose(fpSrc);
LOG_SLP_ERROR_AND_RETURN(SLP_LOG_ERR, "fcopy: could not open both src and dest to copy", SLP_PARAMETER_BAD);
}
else
{
total = st1.st_size;
while (total > 0)
{
int xfer = (total > 100) ? 100 : total;
int got;
int wrote;
if ((got = read(fileno(fpSrc),buf,xfer)) != xfer ||
(wrote = write(fileno(fpDest),buf,xfer)) != xfer)
{
fclose(fpSrc);
fclose(fpDest);
LOG_SLP_ERROR_AND_RETURN(SLP_LOG_ERR, "fcopy: could not read temp or write reg file", SLP_INTERNAL_SYSTEM_ERROR);
}
total -= xfer;
}
if (SDchmod_writable(SLPGetProperty("com.sun.slp.regfile")) < 0)
{
SLP_LOG(SLP_LOG_ERR,"fcopy: could not change file permissions %s",strerror(errno));
fclose(fpSrc);
fclose(fpDest);
return SLP_PREFERENCES_ERROR;
}
if (fclose(fpSrc) || fclose(fpDest))
{
LOG_SLP_ERROR_AND_RETURN(SLP_LOG_ERR, "fcopy: could not close temp or reg file", SLP_INTERNAL_SYSTEM_ERROR);
}
}
return SLP_OK;
}
#endif
static void exit_handler(int signo)
{
#ifdef ENABLE_SLP_LOGGING
SLP_LOG( SLP_LOG_STATE, "*** slpd exit has been called: (%d) ***", signo );
#endif
close(global_resources.sdUDP);
remove(SLPGetProperty("com.sun.slp.tempfile"));
#ifdef SLPTCP
close(global_resources.sdTCP);
#endif
#ifdef ENABLE_SLP_LOGGING
if ( signo == SIGHUP )
{
SLP_LOG( SLP_LOG_SIGNAL, "slpd's parent pid is %d", getppid() );
}
#endif
exit(0);
}