/*
* update.c - dnssec-trigger update
*
* Copyright (c) 2012, NLnet Labs. All rights reserved.
*
* This software is open source.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of the NLNET LABS nor the names of its contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* \file
*
* This file contains the implementation to check and download the update
*/
#include "config.h"
#include "update.h"
#include "probe.h"
#include "http.h"
#include "netevent.h"
#include "log.h"
#include "cfg.h"
#include "svr.h"
#include <ldns/ldns.h>
struct selfupdate* selfupdate_create(struct svr* svr, struct cfg* cfg)
{
struct selfupdate* se = (struct selfupdate*)calloc(1, sizeof(*se));
if(!se) {
log_err("out of memory");
return NULL;
}
se->svr = svr;
se->cfg = cfg;
svr->update_desired = 1;
se->timer = comm_timer_create(svr->base, selfupdate_timeout, se);
if(!se->timer) {
log_err("out of memory");
free(se);
return NULL;
}
return se;
}
/** delete the temporary file */
static void
selfupdate_delete_file(struct selfupdate* se)
{
if(se->download_file) {
(void)unlink(se->download_file);
free(se->download_file);
se->download_file = NULL;
}
}
/** zero and init */
static void selfupdate_init(struct selfupdate* se)
{
se->update_available = 0;
se->user_replied = 0;
se->user_okay = 0;
se->file_available = 0;
outq_delete(se->txt_query);
se->txt_query = NULL;
free(se->version_available);
se->version_available = NULL;
free(se->hash);
se->hash = NULL;
outq_delete(se->addr_4);
se->addr_4 = NULL;
outq_delete(se->addr_6);
se->addr_6 = NULL;
ldns_rr_list_deep_free(se->addr_list_4);
se->addr_list_4 = NULL;
ldns_rr_list_deep_free(se->addr_list_6);
se->addr_list_6 = NULL;
http_get_delete(se->download_http4);
se->download_http4 = NULL;
http_get_delete(se->download_http6);
se->download_http6 = NULL;
selfupdate_delete_file(se);
free(se->filename);
se->filename = NULL;
}
void selfupdate_delete(struct selfupdate* se)
{
if(!se)
return;
selfupdate_init(se);
comm_timer_delete(se->timer);
free(se);
}
/* when timer done, set update-desired again */
void selfupdate_timeout(void* arg)
{
struct selfupdate* se = (struct selfupdate*)arg;
/* this instructs the server to call the selfupdate_start when
* dnssec becomes available */
se->svr->update_desired = 1;
/* if DNSSEC available now, do so now */
svr_check_update(se->svr);
}
/** set retry timer after a failure */
static void
selfupdate_start_retry_timer(struct selfupdate* se)
{
struct timeval tv;
tv.tv_sec = SELFUPDATE_RETRY;
tv.tv_usec = 0;
comm_timer_set(se->timer, &tv);
}
/** set 24h timer after success but no update needed */
static void
selfupdate_start_next_timer(struct selfupdate* se)
{
struct timeval tv;
tv.tv_sec = SELFUPDATE_NEXT_CHECK;
tv.tv_usec = 0;
comm_timer_set(se->timer, &tv);
}
void selfupdate_start(struct selfupdate* se)
{
char* domain = NULL;
char* server = "127.0.0.1";
/* do not start us again, if it fails we turn desired back on. */
if(!se->svr->update_desired) {
/* robust check for double start */
return;
}
se->svr->update_desired = 0;
verbose(VERB_ALGO, "start check for software update");
/* zero some state */
selfupdate_init(se);
/* start lookup of the domain name with version string and hash */
#ifdef USE_WINSOCK
if(se->test_flag)
domain = "win.test."DNSSECTRIGGER_DOMAIN;
else domain = "win.version."DNSSECTRIGGER_DOMAIN;
#elif defined(HOOKS_OSX)
if(se->test_flag)
domain = "osx.test."DNSSECTRIGGER_DOMAIN;
else domain = "osx.version."DNSSECTRIGGER_DOMAIN;
#else
if(se->test_flag)
domain = "src.test."DNSSECTRIGGER_DOMAIN;
else domain = "src.version."DNSSECTRIGGER_DOMAIN;
#endif
verbose(VERB_ALGO, "fetch domain %s TXT", domain);
/* setup TXT query, DO to get AD flag, no CD flag we want to check it */
se->txt_query = outq_create(server, LDNS_RR_TYPE_TXT, domain,
1, NULL, 0, 0, DNS_PORT, 1, 0);
if(!se->txt_query) {
log_err("out of memory, cannot make version txt query");
selfupdate_start_retry_timer(se);
return;
}
}
/** remove "bla" to bla without quotes */
static void remove_quotes(char* str)
{
size_t len;
if(!str || str[0] != '"')
return;
len = strlen(str);
if(len <= 1)
return;
/* remove endquote */
if(str[len-1] == '"')
str[len-1] = 0;
/* remove startquote, end also EOS marker */
memmove(str, str+1, len);
}
/** parse the TXT record into version and hash */
static int selfupdate_parse_rr(struct selfupdate* se, ldns_rr* txt)
{
char* hashstr;
/* free old strings (if necessary) */
free(se->version_available);
se->version_available = NULL;
free(se->hash);
se->hash = NULL;
se->hashlen = 0;
if(ldns_rr_get_type(txt) != LDNS_RR_TYPE_TXT) {
log_err("selfupdate txt record wrong RR type");
return 0;
}
if(ldns_rr_rd_count(txt) < 2) {
log_err("selfupdate txt record has wrong rd count");
return 0;
}
se->version_available = ldns_rdf2str(ldns_rr_rdf(txt, 0));
if(!se->version_available) {
log_err("out of memory");
return 0;
}
hashstr = ldns_rdf2str(ldns_rr_rdf(txt, 1));
if(!hashstr) {
log_err("out of memory");
return 0;
}
remove_quotes(se->version_available);
remove_quotes(hashstr);
/* parse the hash string */
se->hashlen = strlen(hashstr)/2;
if(se->hashlen == 0 || se->hashlen > 8192) {
log_err("selfupdate parse rr bad hash length");
free(hashstr);
return 0;
}
se->hash = (uint8_t*)calloc(1, se->hashlen);
if(ldns_hexstring_to_data(se->hash, hashstr) != (int)se->hashlen) {
log_err("selfupdate failed to parse hash");
free(hashstr);
return 0;
}
verbose(VERB_OPS, "version check %s with hash %s",
se->version_available, hashstr);
free(hashstr);
return 1;
}
/** start HTTP fetch of newer version */
static int
selfupdate_start_http_fetch(struct selfupdate* se)
{
char* server = "127.0.0.1";
char* domain = DNSSECTRIGGER_DOWNLOAD_HOST;
/* get ip4 and ip6 simultaneously, with addr lookup and http_get */
se->addr_4 = outq_create(server, LDNS_RR_TYPE_A, domain, 1, NULL,
0, 0, DNS_PORT, 1, 0);
se->addr_6 = outq_create(server, LDNS_RR_TYPE_AAAA, domain, 1, NULL,
0, 0, DNS_PORT, 1, 0);
if(!se->addr_4 && !se->addr_6) {
log_info("failed to create address lookups for download");
return 0;
}
/* one of the address lookups works, so keep going */
return 1;
}
/** see if version x is newer than y */
int
version_is_newer(const char* x, const char* y)
{
const char* xat = x, *yat = y;
/* version in NLnetLabs format:
* 1.2.3 (or 1.2.3.4)
* 1.2 is newer than 1.0
* 1.2.3 is newer than 1.2
* 1.2.3rc1 (for release candidates, 1.2.3 is better)
* 1.2.3_20120101 (for snapshots, with date, full 1.2.3 is better) */
/* returns true if cannot determine and it is different */
if(x[0] == 0)
return 0; /* the empty version is no fun, do not prefer it */
/* for the part before the rc or _, compare the numbers every dot */
while(xat[0] && yat[0]) {
char* xend, *yend;
long xnr = strtol(xat, &xend, 10);
long ynr = strtol(yat, ¥d, 10);
/* difference at this point */
if(xnr != ynr) {
return (xnr > ynr);
}
xat = xend;
yat = yend;
if(xat[0] != '.') break;
if(yat[0] != '.') break;
xat++;
yat++;
}
/* one is longer than the other? */
if(xat[0] == 0 && yat[0] == 0)
return 0; /* equal */
if(xat[0] == '.')
return 1; /* x=1.2.3 y=1.2 and x is newer */
if(yat[0] == '.')
return 0; /* x=1.2 y=1.2.3, and thus not newer */
if(xat[0] == 0 && yat[0] != 0)
return 1; /* x=1.2 y=1.2[rcorsnap] and thus x is newer */
if(xat[0] != 0 && yat[0] == 0)
return 0; /* x=1.2[rcorsnap] y=1.2 and thus x is not newer */
/* both xat and yat are not at eos */
if(strncmp(xat, "rc", 2)==0 && strncmp(yat, "rc", 2)==0) {
/* compare rc versions */
char* xend, *yend;
long xrc = strtol(xat+2, &xend, 10);
long yrc = strtol(yat+2, ¥d, 10);
/* check that it ends correctly, otherwise it is unknown */
if(xend[0]==0 && yend[0]==0)
return (xrc > yrc);
} else if(xat[0]=='_' && yat[0]=='_') {
/* compare snapshots 20110304, 20120506 and so on */
char* xend, *yend;
long xsnap = strtol(xat+1, &xend, 10);
long ysnap = strtol(yat+1, ¥d, 10);
/* check that it ends correctly, otherwise it is unknown */
if(xend[0]==0 && yend[0]==0)
return (xsnap > ysnap);
}
/* not a clue how to compare, such as rc with snapshot, if its
* different you want to update */
if(strcmp(xat, yat) != 0)
return 1;
return 0;
}
/*
* The TXT query is done.
*/
static void selfupdate_outq_done_txt(struct selfupdate* se, struct outq* outq,
ldns_pkt* pkt, const char* reason)
{
ldns_rr_list* txt;
verbose(VERB_ALGO, "selfupdate %s done: %s", outq->qname,
reason?reason:"success");
outq_delete(se->txt_query);
se->txt_query = NULL;
if(reason || !pkt) {
/* it failed */
ldns_pkt_free(pkt); /* in case there is a packet */
selfupdate_start_retry_timer(se);
return;
}
/* check AD flag */
if(!ldns_pkt_ad(pkt)) {
log_err("selfupdate TXT without AD flag encountered, skip");
ldns_pkt_free(pkt);
selfupdate_start_retry_timer(se);
return;
}
/* get TXT record */
txt = ldns_pkt_rr_list_by_type(pkt, LDNS_RR_TYPE_TXT,
LDNS_SECTION_ANSWER);
if(!txt || ldns_rr_list_rr_count(txt) == 0) {
log_err("selfupdate answer has AD flag, but no TXT");
ldns_rr_list_deep_free(txt);
ldns_pkt_free(pkt);
selfupdate_start_retry_timer(se);
return;
}
/* parse version and hash from it */
if(!selfupdate_parse_rr(se, ldns_rr_list_rr(txt, 0))) {
log_err("cannot parse selfupdate TXT rr, skip");
ldns_rr_list_deep_free(txt);
ldns_pkt_free(pkt);
selfupdate_start_retry_timer(se);
return;
}
/* update the time */
se->last_check = time(NULL);
ldns_rr_list_deep_free(txt);
ldns_pkt_free(pkt);
/* see what we need to do now */
if(version_is_newer(se->version_available, PACKAGE_VERSION)) {
/* start http fetch */
verbose(VERB_OPS, "version %s available, starting download",
se->version_available);
if(!selfupdate_start_http_fetch(se)) {
log_err("selfupdate cannot start http fetch");
selfupdate_start_retry_timer(se);
return;
}
} else {
verbose(VERB_ALGO, "version %s available (it is not newer)",
se->version_available);
selfupdate_start_next_timer(se);
}
}
/*
* Initiate file download from http
*/
static int selfupdate_start_file_download(struct selfupdate* se,
ldns_rr_list* addr)
{
char url[256];
char file[256];
char* reason = NULL;
struct http_get** handle;
char* ipstr;
ldns_rr* rr;
file[0]=0;
url[0]=0;
/* pick the next address (randomly) */
rr = http_pick_random_addr(addr);
if(!rr || !ldns_rr_rdf(rr, 0)) {
log_err("addr without rdata");
ldns_rr_free(rr);
return 0;
}
ipstr = ldns_rdf2str(ldns_rr_rdf(rr, 0));
if(!ipstr) {
log_err("out of memory");
ldns_rr_free(rr);
return 0;
}
/* create the URL */
#ifdef HOOKS_OSX
snprintf(file, sizeof(file), "dnssectrigger-%s.dmg",
se->version_available);
#elif defined(USE_WINSOCK)
snprintf(file, sizeof(file), "dnssec_trigger_setup_%s.exe",
se->version_available);
#else /* UNIX */
snprintf(file, sizeof(file), "dnssec-trigger-%s.tar.gz",
se->version_available);
#endif
snprintf(url, sizeof(url), "http://%s%s%s%s",
DNSSECTRIGGER_DOWNLOAD_HOST,
DNSSECTRIGGER_DOWNLOAD_URLPRE,
se->test_flag?"test/":"",
file);
if(!(se->filename=strdup(file))) {
log_err("out of memory");
ldns_rr_free(rr);
free(ipstr);
return 0;
}
/* start http_get */
verbose(VERB_ALGO, "fetch %s from %s", url, ipstr);
if(ldns_rr_get_type(rr) == LDNS_RR_TYPE_A)
handle = &se->download_http4;
else handle = &se->download_http6;
ldns_rr_free(rr);
http_get_delete(*handle);
*handle = http_get_create(url, se->svr->base, NULL);
if(!*handle) {
log_err("out of memory");
http_get_delete(*handle);
*handle = NULL;
free(ipstr);
return 0;
}
if(!http_get_fetch(*handle, ipstr, HTTP_PORT, &reason)) {
log_err("update fetch failed: %s", reason?reason:"fail");
http_get_delete(*handle);
*handle = NULL;
free(ipstr);
return 0;
}
free(ipstr);
return 1;
}
/** attempt next address in the selfupdate addr list */
static int
selfupdate_next_addr(struct selfupdate* se, ldns_rr_list* list)
{
while(list && ldns_rr_list_rr_count(list)) {
if(selfupdate_start_file_download(se, list)) {
return 1;
}
}
return 0;
}
/** check hash on data segment */
static int
software_hash_ok(struct selfupdate* se, struct http_get* hg)
{
unsigned char download_hash[LDNS_SHA256_DIGEST_LENGTH];
if(se->hashlen != LDNS_SHA256_DIGEST_LENGTH) {
log_err("bad hash length from TXT record %d", (int)se->hashlen);
return 0;
}
(void)ldns_sha256((unsigned char*)ldns_buffer_begin(hg->data),
(unsigned int)ldns_buffer_limit(hg->data), download_hash);
if(memcmp(download_hash, se->hash, se->hashlen) != 0) {
log_err("hash mismatch:");
log_hex("download", download_hash, sizeof(download_hash));
log_hex("txtindns", se->hash, se->hashlen);
return 0;
}
verbose(VERB_ALGO, "downloaded file sha256 is OK");
return 1;
}
/** write to temporary file */
static int
selfupdate_write_file(struct selfupdate* se, struct http_get* hg)
{
char buf[1024];
FILE *out;
/* get directory to store the file into */
#ifdef HOOKS_OSX
char* dirname = UIDIR;
char* slash="/";
#elif defined(USE_WINSOCK)
char* dirname = w_lookup_reg_str("Software\\Unbound", "InstallLocation");
char* slash="\\";
if(!dirname) dirname = strdup(UIDIR);
if(!dirname) { log_err("out of memory"); return 0; }
#else /* UNIX */
char* dirname = "/tmp";
char* slash="/";
#endif
snprintf(buf, sizeof(buf), "%s%s%s", dirname, slash, se->filename);
if(se->download_file)
selfupdate_delete_file(se);
se->download_file = strdup(buf);
if(!se->download_file) {
log_err("out of memory");
fail:
selfupdate_delete_file(se);
#ifdef USE_WINSOCK
free(dirname);
#endif
return 0;
}
out = fopen(se->download_file, "wb");
if(!out) {
log_err("cannot open file %s: %s", se->download_file,
strerror(errno));
goto fail;
}
if(!fwrite(ldns_buffer_begin(hg->data), 1, ldns_buffer_limit(hg->data),
out)) {
log_err("cannot write to file %s: %s", se->download_file,
strerror(errno));
fclose(out);
goto fail;
}
fclose(out);
#ifdef USE_WINSOCK
free(dirname);
#endif
return 1;
}
static void stop_other_http(struct selfupdate* se, struct http_get* hg)
{
if(hg == se->download_http4) {
outq_delete(se->addr_6);
se->addr_6 = NULL;
ldns_rr_list_deep_free(se->addr_list_6);
se->addr_list_6 = NULL;
http_get_delete(se->download_http6);
se->download_http6 = NULL;
} else {
outq_delete(se->addr_4);
se->addr_4 = NULL;
ldns_rr_list_deep_free(se->addr_list_4);
se->addr_list_4 = NULL;
http_get_delete(se->download_http4);
se->download_http4 = NULL;
}
}
void selfupdate_http_connected(struct selfupdate* se, struct http_get* hg)
{
/* we do not need the other one any more (happy eyeballs) */
stop_other_http(se, hg);
}
void
selfupdate_http_get_done(struct selfupdate* se, struct http_get* hg,
char* reason)
{
ldns_rr_list* list = (hg == se->download_http4)?
se->addr_list_4:se->addr_list_6;
struct http_get** handle = (hg == se->download_http4)?
&se->download_http4:&se->download_http6;
verbose(VERB_ALGO, "selfupdate download done %s",
reason?reason:"success");
if(reason) {
fail:
/* try next address or fail completely */
if(selfupdate_next_addr(se, list))
return;
/* we failed, see if the other is active or done */
if(hg == se->download_http4 && se->addr_6) {
outq_delete(se->addr_4);
se->addr_4 = NULL;
} else if(hg == se->download_http6 && se->addr_4) {
outq_delete(se->addr_6);
se->addr_6 = NULL;
} else {
selfupdate_start_retry_timer(se);
}
http_get_delete(*handle);
*handle = NULL;
return;
}
verbose(VERB_ALGO, "done with success");
ldns_buffer_flip(hg->data);
/* check data integrity */
if(!software_hash_ok(se, hg)) {
log_err("bad hash on download of %s from %s", hg->url, hg->dest);
goto fail;
}
/* stop the other attempt (if any) */
stop_other_http(se, hg);
if(!selfupdate_write_file(se, hg)) {
selfupdate_start_retry_timer(se);
http_get_delete(*handle);
*handle = NULL;
return;
}
se->file_available = 1;
http_get_delete(*handle);
*handle = NULL;
/* go and ask the user for permission */
se->update_available = 1;
/* signal panel and get return command */
svr_signal_update(se->svr, se->version_available);
}
/*
* The addr (A, AAAA) query is done.
*/
static void selfupdate_outq_done_addr(struct selfupdate* se, struct outq* outq,
ldns_pkt* pkt, const char* reason)
{
ldns_rr_list* addr;
verbose(VERB_ALGO, "selfupdate addr%s %s done: %s",
outq->qtype==LDNS_RR_TYPE_A?"4":"6",
outq->qname, reason?reason:"success");
if(reason || !pkt) {
failed:
/* it failed */
ldns_pkt_free(pkt); /* in case there is a packet */
/* see if the other query is active */
if(outq==se->addr_4 && se->addr_6) {
outq_delete(outq);
se->addr_4 = NULL;
} else if(outq==se->addr_6 && se->addr_4) {
outq_delete(outq);
se->addr_6 = NULL;
} else {
selfupdate_start_retry_timer(se);
}
return;
}
/* check AD flag */
if(!ldns_pkt_ad(pkt)) {
log_err("selfupdate addr without AD flag encountered, skip");
goto failed;
}
/* get addresses */
addr = ldns_pkt_rr_list_by_type(pkt, outq->qtype, LDNS_SECTION_ANSWER);
if(!addr || ldns_rr_list_rr_count(addr) == 0) {
log_err("selfupdate answer has AD flag, but no TXT");
ldns_rr_list_deep_free(addr);
goto failed;
}
if(outq->qtype == LDNS_RR_TYPE_A) {
ldns_rr_list_deep_free(se->addr_list_4);
se->addr_list_4 = addr;
} else {
ldns_rr_list_deep_free(se->addr_list_6);
se->addr_list_6 = addr;
}
ldns_pkt_free(pkt);
pkt = NULL;
/* start the download of the file */
if(!selfupdate_next_addr(se, addr)) {
log_err("selfupdate could not initiate file download");
goto failed;
}
}
void selfupdate_outq_done(struct selfupdate* se, struct outq* outq,
ldns_pkt* pkt, const char* reason)
{
if(se->txt_query == outq)
selfupdate_outq_done_txt(se, outq, pkt, reason);
else if(se->addr_4 == outq || se->addr_6 == outq)
selfupdate_outq_done_addr(se, outq, pkt, reason);
else {
log_err("internal error: selfupdate unknown outq? leaked");
log_info("reason: %s, outq %s %d %s %s %s",
reason?reason:"null",
outq->qname, (int)outq->qtype,
outq->edns?"edns":"noedns",
outq->cdflag?"cdflag":"nocdflag",
outq->on_tcp?"tcp":"udp");
/* and ignore this to continue ... */
}
}
#ifdef HOOKS_OSX
/* close sockets on svr */
static void
svr_close_sockets(struct svr* svr)
{
struct sslconn* b;
struct listen_list* l;
struct probe_ip* p;
/* do not cause traffic on the fds because the real daemon
* is still using them */
for(b=svr->busy_list; b; b=b->next) {
if(b->c)
close(b->c->fd);
}
for(l=svr->listen; l; l=l->next) {
if(l->c)
close(l->c->fd);
}
for(p=svr->probes; p; p=p->next) {
if(p->ds_c && p->ds_c->c)
close(p->ds_c->c->fd);
if(p->dnskey_c && p->dnskey_c->c)
close(p->dnskey_c->c->fd);
if(p->nsec3_c && p->nsec3_c->c)
close(p->nsec3_c->c->fd);
if(p->host_c && p->host_c->c)
close(p->host_c->c->fd);
if(p->http && p->http->cp)
close(p->http->cp->fd);
}
if(svr->update) {
if(svr->update->download_http4 &&
svr->update->download_http4->cp)
close(svr->update->download_http4->cp->fd);
if(svr->update->download_http6 &&
svr->update->download_http6->cp)
close(svr->update->download_http6->cp->fd);
if(svr->update->txt_query && svr->update->txt_query->c)
close(svr->update->txt_query->c->fd);
if(svr->update->addr_4 && svr->update->addr_4->c)
close(svr->update->addr_4->c->fd);
if(svr->update->addr_6 && svr->update->addr_6->c)
close(svr->update->addr_6->c->fd);
}
}
/* fork and exec the script that opens the dmg and runs installer */
static void
osx_run_updater(char* filename)
{
pid_t pid = fork();
switch(pid) {
default: /* main */
return;
case -1: /* error */
log_err("cannot fork installscript: %s", strerror(errno));
return;
case 0: /* child */
break;
}
/* close our sockets */
svr_close_sockets(global_svr);
/* become process leader so we are not killed by installer unload of
* plist file */
if(setsid() == -1)
log_err("setsid failed: %s", strerror(errno));
/* run the install script */
if(execl(LIBEXEC_DIR "/dnssec-trigger-setdns.sh",
LIBEXEC_DIR "/dnssec-trigger-setdns.sh", "install", filename,
(char*)0) < 0) {
log_err("cannot exec setdns install: %s", strerror(errno));
}
exit(1);
}
#endif /* HOOKS_OSX */
/** do the software update install (system specific) */
static void
selfupdate_do_install(struct selfupdate* se)
{
if(!se) return;
if(!se->update_available) return;
if(!se->file_available || !se->download_file) return;
verbose(VERB_OPS, "software update, install of %s", se->download_file);
if(se->svr->cfg->noaction) {
verbose(VERB_OPS, "noaction is true, no install action");
return;
}
#ifdef USE_WINSOCK
/* fork and exec the installer that will stop this program and update*/
/* Do not run updater from service, but from userspace, so that
* tray icon gets started correctly and so on, and reboot warn dialog*/
/*win_run_updater(se->download_file);*/
/* this stops the filename from being deleted when we exit,
* the installer deletes itself (with after reboot flag). */
free(se->download_file);
se->download_file = NULL;
se->file_available = 0;
#elif defined(HOOKS_OSX)
/* fork and exec installer */
osx_run_updater(se->download_file);
/* stops filename from deleted by this daemon on exit, since
* the postinstall makes us exit and the installer is deleted by
* the fork-exec-ed script */
free(se->download_file);
se->download_file = NULL;
se->file_available = 0;
#else
log_err("on unix, do not know how to install. tarball %s (is deleted"
"on exit of the daemon)", se->download_file);
#endif
}
void selfupdate_userokay(struct selfupdate* se, int okay)
{
if(!se)
return;
if(se->user_replied)
return; /* already replied, this is a duplicate */
/* is the user OK ? */
/* if not, note so we do not ask him again for 24h or next start */
se->user_replied = 1;
se->user_okay = okay;
/* if OK run installer (fork,exec because it updates this one) */
if(se->user_okay) {
selfupdate_do_install(se);
}
}