|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include "global.h" |
#include "config.h" |
#include "pike_macros.h" |
#include "stralloc.h" |
#include "pike_error.h" |
#include "object.h" |
#include "constants.h" |
#include "interpret.h" |
#include "svalue.h" |
#include "mapping.h" |
#include "builtin_functions.h" |
#include "module_support.h" |
#include "threads.h" |
|
#include <signal.h> |
|
|
#ifdef THIS |
#undef THIS |
#endif |
#define THIS ((struct service *)(Pike_fp->current_storage)) |
#define sp Pike_sp |
|
|
#if defined(HAVE_DNS_SD) || defined(HAVE_HOWL) |
|
|
|
|
|
#ifdef HAVE_DNS_SD |
|
|
#ifdef HAVE_DNS_SD_H |
|
|
#define kDNSServiceErr_BadinterfaceIndex kDNSServiceErr_BadInterfaceIndex |
|
#include <dns_sd.h> |
#endif |
|
#define IS_ERR(x) ((x) != kDNSServiceErr_NoError) |
|
|
struct service { |
DNSServiceRef service_ref; |
}; |
|
|
static void raise_error(char *msg, DNSServiceErrorType err) |
{ |
char *reason; |
|
if (err == kDNSServiceErr_NoError) |
return; |
|
switch (err) { |
case kDNSServiceErr_NoSuchName: |
reason = "No such name"; |
break; |
case kDNSServiceErr_NoMemory: |
reason = "No memory"; |
break; |
case kDNSServiceErr_BadParam: |
reason = "Bad parameter"; |
break; |
case kDNSServiceErr_BadReference: |
reason = "Bad reference"; |
break; |
case kDNSServiceErr_BadState: |
reason = "Bad state"; |
break; |
case kDNSServiceErr_BadFlags: |
reason = "Bad flags"; |
break; |
case kDNSServiceErr_Unsupported: |
reason = "Unsupported"; |
break; |
case kDNSServiceErr_AlreadyRegistered: |
reason = "Already registered"; |
break; |
case kDNSServiceErr_NameConflict: |
reason = "Name conflict"; |
break; |
case kDNSServiceErr_Invalid: |
reason = "Invalid"; |
break; |
case kDNSServiceErr_Incompatible: |
reason = "Incompatible"; |
break; |
case kDNSServiceErr_BadInterfaceIndex: |
reason = "Bad interface index"; |
break; |
default: |
reason = "Unknown error"; |
break; |
} |
Pike_error("DNS_SD: %s Reason: %s (%d)\n", msg, reason, err); |
} |
|
|
static void start_service_callback(DNSServiceRef ref, |
DNSServiceFlags flags, |
DNSServiceErrorType error, |
const char *name, |
const char *regtype, |
const char *domain, |
void *context) |
{ |
} |
|
|
static DNSServiceErrorType start_service(struct service *svc, |
char *name, |
char *service, |
char *domain, |
int port, |
char *txt, |
int txtlen) |
{ |
DNSServiceErrorType err; |
DNSServiceRef ref; |
|
|
if (name && !strlen(name)) |
name = NULL; |
if (domain && !strlen(domain)) |
domain = NULL; |
if (txt && !txtlen) |
txt = NULL; |
|
svc->service_ref = NULL; |
err = DNSServiceRegister(&ref, 0, 0, name, service, domain, NULL, port, |
txtlen, txt, start_service_callback, NULL); |
if (err == kDNSServiceErr_NoError) |
svc->service_ref = ref; |
|
err = DNSServiceProcessResult(ref); |
return err; |
} |
|
|
static void stop_service(struct service *svc) |
{ |
if (svc->service_ref) { |
DNSServiceRefDeallocate(svc->service_ref); |
svc->service_ref = NULL; |
} |
} |
|
|
static DNSServiceErrorType update_txt_record(struct service *svc, |
char *txt, int txtlen) |
{ |
if (svc->service_ref) { |
int ttl = 0; |
return DNSServiceUpdateRecord(svc->service_ref, NULL, 0, |
txtlen, txt, ttl); |
} |
return kDNSServiceErr_Invalid; |
} |
|
#endif /* HAVE_DNS_SD */ |
|
|
|
|
#if defined(HAVE_HOWL) && !defined(HAVE_DNS_SD) |
|
|
#ifdef HAVE_HOWL_H |
#include <howl.h> |
#endif |
|
#define IS_ERR(x) ((x) != SW_OKAY) |
|
|
static sw_discovery service_session = NULL; |
static THREAD_T service_thread; |
|
struct service { |
sw_discovery_oid service_ref; |
}; |
|
|
static void raise_error(char *msg, sw_result err) |
{ |
char *reason; |
|
if (err == SW_OKAY) |
return; |
|
switch (err) { |
case SW_DISCOVERY_E_NO_MEM: |
reason = "No memory"; |
break; |
case SW_DISCOVERY_E_BAD_PARAM: |
reason = "Bad parameter"; |
break; |
case SW_DISCOVERY_E_UNKNOWN: |
default: |
reason = "Unknown error"; |
break; |
} |
Pike_error("DNS_SD: %s Reason: %s (%d)\n", msg, reason, err); |
} |
|
|
|
static sw_result start_reply_fn(sw_discovery discovery, |
sw_discovery_publish_status status, |
sw_discovery_oid oid, |
sw_opaque extra) |
{ |
|
return SW_OKAY; |
} |
|
|
static sw_result start_service(struct service *svc, |
char *name, |
char *service, |
char *domain, |
int port, |
char *txt, |
int txtlen) |
{ |
sw_result err = SW_DISCOVERY_E_UNKNOWN; |
|
|
if (domain && !strlen(domain)) |
domain = NULL; |
if (txt && !txtlen) |
txt = NULL; |
|
err = sw_discovery_publish(service_session, |
0, name, service, domain, NULL, port, |
txt, txtlen, start_reply_fn, NULL, |
&svc->service_ref); |
return err; |
} |
|
|
static void stop_service(struct service *svc) |
{ |
if (svc->service_ref) { |
|
sw_discovery_cancel(service_session, svc->service_ref); |
} |
} |
|
|
static sw_result update_txt_record(struct service *svc, char *txt, int txtlen) |
{ |
if (svc->service_ref) { |
return sw_discovery_publish_update(service_session, |
svc->service_ref, |
txt, txtlen); |
} |
return SW_DISCOVERY_E_UNKNOWN; |
} |
|
|
static void * howl_thread(void *arg) |
{ |
sw_discovery_run(service_session); |
return NULL; |
} |
|
|
static void init_howl_module() |
{ |
if (sw_discovery_init(&service_session) == SW_OKAY) { |
th_create_small(&service_thread, howl_thread, NULL); |
} |
} |
|
|
static void exit_howl_module() |
{ |
|
if (service_session) |
sw_discovery_fina(service_session); |
|
|
if (service_thread) |
th_kill(service_thread, SIGCHLD); |
} |
|
|
#endif /* defined(HAVE_HOWL) && !defined(HAVE_DNS_SD) */ |
|
|
|
|
static void f_update_txt(INT32 args) |
{ |
check_all_args("Service->update_txt", args, |
BIT_STRING, |
0); |
|
|
if (THIS->service_ref) { |
char *txt = sp[0 - args].u.string->str; |
int txtlen = sp[0 - args].u.string->len; |
|
int err = update_txt_record(THIS, txt, txtlen); |
if (IS_ERR(err)) |
raise_error("Could not update TXT record.", err); |
} |
pop_n_elems(args); |
} |
|
|
static void f_create(INT32 args) |
{ |
char *name, *service, *domain, *txt; |
int port, txtlen, err; |
|
check_all_args("Service->create", args, |
BIT_STRING, |
BIT_STRING, |
BIT_STRING, |
BIT_INT, |
BIT_STRING | BIT_VOID, |
0); |
|
|
stop_service(THIS); |
|
|
|
name = sp[0 - args].u.string->str; |
service = sp[1 - args].u.string->str; |
domain = sp[2 - args].u.string->str; |
port = sp[3 - args].u.integer; |
|
|
|
txt = (args == 5) ? sp[4 - args].u.string->str : NULL; |
txtlen = txt ? sp[4 - args].u.string->len : 0; |
|
|
err = start_service(THIS, name, service, domain, port, txt, txtlen); |
if (IS_ERR(err)) |
raise_error("Could not register service.", err); |
pop_n_elems(args); |
} |
|
|
static void init_service_struct(struct object *o) |
{ |
THIS->service_ref = 0; |
} |
|
|
static void exit_service_struct(struct object *o) |
{ |
|
stop_service(THIS); |
} |
|
|
PIKE_MODULE_INIT |
{ |
start_new_program(); |
|
ADD_STORAGE(struct service); |
|
set_init_callback(init_service_struct); |
set_exit_callback(exit_service_struct); |
|
|
ADD_FUNCTION("create", f_create, |
tFunc(tStr tStr tStr tInt tOr(tStr, tVoid), tVoid), 0); |
|
|
ADD_FUNCTION("update_txt", f_update_txt, tFunc(tStr, tVoid), 0); |
|
end_class("Service", 0); |
|
#ifdef HAVE_HOWL |
init_howl_module(); |
#endif |
} |
|
|
PIKE_MODULE_EXIT |
{ |
#ifdef HAVE_HOWL |
exit_howl_module(); |
#endif |
} |
|
|
#else /* defined(HAVE_DNS_SD) || defined(HAVE_HOWL) */ |
|
PIKE_MODULE_INIT {} |
PIKE_MODULE_EXIT {} |
|
#endif /* defined(HAVE_DNS_SD) || defined(HAVE_HOWL) */ |
|
|