update esppilight lib
content of the library is not filtered on 433mhz only
This commit is contained in:
1 parent
a275e48ee1
commit
67dd7ac078
1030 files changed
+203335
-116
No files matched your search
@@ -0,0 +1,23 @@
|
||||
project(libs)
|
||||
|
||||
include(../CMakeConfig.txt)
|
||||
|
||||
if(${MODULESPACK} MATCHES "OFF" OR WIN32)
|
||||
set(${PROJECT_NAME}_files "" CACHE INTERNAL "")
|
||||
|
||||
add_subdirectory(libuv)
|
||||
set(${PROJECT_NAME}_files ${${PROJECT_NAME}_files} ${libuv_files} CACHE INTERNAL "")
|
||||
|
||||
add_subdirectory(avrdude)
|
||||
set(${PROJECT_NAME}_files ${${PROJECT_NAME}_files} ${avrdude_files} CACHE INTERNAL "")
|
||||
|
||||
if(${ZWAVE} MATCHES "ON")
|
||||
add_subdirectory(openzwave)
|
||||
set(${PROJECT_NAME}_files ${${PROJECT_NAME}_files} ${openzwave_files} CACHE INTERNAL "")
|
||||
endif()
|
||||
|
||||
add_subdirectory(pilight)
|
||||
set(${PROJECT_NAME}_files ${${PROJECT_NAME}_files} ${pilight_files} CACHE INTERNAL "")
|
||||
else()
|
||||
add_subdirectory(pilight)
|
||||
endif()
|
||||
@@ -0,0 +1,10 @@
|
||||
project(avrdude)
|
||||
|
||||
file(GLOB ${PROJECT_NAME}_sources "${PROJECT_SOURCE_DIR}/*.c")
|
||||
file(GLOB ${PROJECT_NAME}_headers "${PROJECT_SOURCE_DIR}/*.h")
|
||||
|
||||
if(WIN32)
|
||||
list(REMOVE_ITEM ${PROJECT_NAME}_sources "${PROJECT_SOURCE_DIR}/avrgpio.c")
|
||||
endif()
|
||||
|
||||
set(${PROJECT_NAME}_files ${${PROJECT_NAME}_sources} ${${PROJECT_NAME}_headers} CACHE INTERNAL "")
|
||||
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* avrdude - A Downloader/Uploader for AVR device programmers
|
||||
* Copyright (C) 2009 Lars Immisch
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* $Id: arduino.c 1294 2014-03-12 23:03:18Z joerg_wunsch $ */
|
||||
|
||||
/*
|
||||
* avrdude interface for Arduino programmer
|
||||
*
|
||||
* The Arduino programmer is mostly a STK500v1, just the signature bytes
|
||||
* are read differently.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "avrdude.h"
|
||||
#include "pgm.h"
|
||||
#include "stk500_private.h"
|
||||
#include "stk500.h"
|
||||
#include "serial.h"
|
||||
#include "arduino.h"
|
||||
#include "../pilight/core/log.h"
|
||||
|
||||
/* read signature bytes - arduino version */
|
||||
static int arduino_read_sig_bytes(PROGRAMMER *pgm, AVRPART *p, AVRMEM *m) {
|
||||
unsigned char buf[32];
|
||||
|
||||
/* Signature byte reads are always 3 bytes. */
|
||||
|
||||
if(m->size < 3) {
|
||||
logprintf(LOG_ERR, "memsize too small for sig byte read");
|
||||
return -1;
|
||||
}
|
||||
|
||||
buf[0] = Cmnd_STK_READ_SIGN;
|
||||
buf[1] = Sync_CRC_EOP;
|
||||
|
||||
serial_send(&pgm->fd, buf, 2);
|
||||
|
||||
if(serial_recv(&pgm->fd, buf, 5) < 0) {
|
||||
return -1;
|
||||
}
|
||||
if(buf[0] == Resp_STK_NOSYNC) {
|
||||
logprintf(LOG_ERR, "stk500_cmd(): programmer is out of sync");
|
||||
return -1;
|
||||
} else if (buf[0] != Resp_STK_INSYNC) {
|
||||
logprintf(LOG_ERR, "arduino_read_sig_bytes(): (a) protocol error, expect=0x%02x, resp=0x%02x", Resp_STK_INSYNC, buf[0]);
|
||||
return -2;
|
||||
}
|
||||
if(buf[4] != Resp_STK_OK) {
|
||||
logprintf(LOG_ERR, "arduino_read_sig_bytes(): (a) protocol error, expect=0x%02x, resp=0x%02x", Resp_STK_OK, buf[4]);
|
||||
return -3;
|
||||
}
|
||||
|
||||
m->buf[0] = buf[1];
|
||||
m->buf[1] = buf[2];
|
||||
m->buf[2] = buf[3];
|
||||
|
||||
return 3;
|
||||
}
|
||||
|
||||
static int arduino_open(PROGRAMMER * pgm, char * port) {
|
||||
union pinfo pinfo;
|
||||
strcpy(pgm->port, port);
|
||||
pinfo.baud = pgm->baudrate? pgm->baudrate: 115200;
|
||||
|
||||
if(serial_open(port, pinfo, &pgm->fd) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Clear DTR and RTS to unload the RESET capacitor
|
||||
* (for example in Arduino) */
|
||||
serial_set_dtr_rts(&pgm->fd, 0);
|
||||
usleep(250*1000);
|
||||
/* Set DTR and RTS back to high */
|
||||
serial_set_dtr_rts(&pgm->fd, 1);
|
||||
usleep(50*1000);
|
||||
|
||||
/*
|
||||
* drain any extraneous input
|
||||
*/
|
||||
stk500_drain(pgm, 0);
|
||||
|
||||
if(stk500_getsync(pgm) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void arduino_close(PROGRAMMER * pgm) {
|
||||
serial_set_dtr_rts(&pgm->fd, 0);
|
||||
serial_close(&pgm->fd);
|
||||
pgm->fd.ifd = -1;
|
||||
}
|
||||
|
||||
const char arduino_desc[] = "Arduino programmer";
|
||||
|
||||
void arduino_initpgm(PROGRAMMER * pgm) {
|
||||
/* This is mostly a STK500; just the signature is read
|
||||
differently than on real STK500v1
|
||||
and the DTR signal is set when opening the serial port
|
||||
for the Auto-Reset feature */
|
||||
stk500_initpgm(pgm);
|
||||
|
||||
strcpy(pgm->type, "Arduino");
|
||||
pgm->read_sig_bytes = arduino_read_sig_bytes;
|
||||
pgm->open = arduino_open;
|
||||
pgm->close = arduino_close;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* avrdude - A Downloader/Uploader for AVR device programmers
|
||||
* Copyright (C) 2009 Lars Immisch
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* $Id: arduino.h 1294 2014-03-12 23:03:18Z joerg_wunsch $ */
|
||||
|
||||
#ifndef arduino_h__
|
||||
#define arduino_h__
|
||||
|
||||
extern const char arduino_desc[];
|
||||
void arduino_initpgm (PROGRAMMER * pgm);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -0,0 +1,915 @@
|
||||
/*
|
||||
* avrdude - A Downloader/Uploader for AVR device programmers
|
||||
* Copyright (C) 2000-2004 Brian S. Dean <bsd@bsdhome.com>
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
/* $Id: avr.c 907 2010-01-12 15:42:40Z joerg_wunsch $ */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "avrdude.h"
|
||||
|
||||
#include "../pilight/core/log.h"
|
||||
#include "../pilight/core/common.h"
|
||||
#include "avr.h"
|
||||
#include "pindefs.h"
|
||||
#include "safemode.h"
|
||||
|
||||
FP_UpdateProgress avr_update_progress;
|
||||
|
||||
void avr_set_update_progress(FP_UpdateProgress callback) {
|
||||
avr_update_progress = callback;
|
||||
}
|
||||
|
||||
int parse_cmdbits(OPCODE * op, char *t) {
|
||||
int bitno;
|
||||
unsigned int n = 0, i = 0;
|
||||
char ch;
|
||||
char *e = NULL;
|
||||
char *q = NULL;
|
||||
int len;
|
||||
char **array = NULL;
|
||||
|
||||
bitno = 32;
|
||||
n = explode(t, " ", &array);
|
||||
for(i=0;i<n;i++) {
|
||||
bitno--;
|
||||
len = strlen(array[i]);
|
||||
ch = array[i][0];
|
||||
|
||||
if(len == 1) {
|
||||
switch(ch) {
|
||||
case '1':
|
||||
op->bit[bitno].type = AVR_CMDBIT_VALUE;
|
||||
op->bit[bitno].value = 1;
|
||||
op->bit[bitno].bitno = bitno % 8;
|
||||
break;
|
||||
case '0':
|
||||
op->bit[bitno].type = AVR_CMDBIT_VALUE;
|
||||
op->bit[bitno].value = 0;
|
||||
op->bit[bitno].bitno = bitno % 8;
|
||||
break;
|
||||
case 'x':
|
||||
op->bit[bitno].type = AVR_CMDBIT_IGNORE;
|
||||
op->bit[bitno].value = 0;
|
||||
op->bit[bitno].bitno = bitno % 8;
|
||||
break;
|
||||
case 'a':
|
||||
op->bit[bitno].type = AVR_CMDBIT_ADDRESS;
|
||||
op->bit[bitno].value = 0;
|
||||
op->bit[bitno].bitno = 8*(bitno/8) + bitno % 8;
|
||||
break;
|
||||
case 'i':
|
||||
op->bit[bitno].type = AVR_CMDBIT_INPUT;
|
||||
op->bit[bitno].value = 0;
|
||||
op->bit[bitno].bitno = bitno % 8;
|
||||
break;
|
||||
case 'o':
|
||||
op->bit[bitno].type = AVR_CMDBIT_OUTPUT;
|
||||
op->bit[bitno].value = 0;
|
||||
op->bit[bitno].bitno = bitno % 8;
|
||||
break;
|
||||
default :
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if(ch == 'a') {
|
||||
q = &array[i][1];
|
||||
op->bit[bitno].bitno = strtol(q, &e, 0);
|
||||
op->bit[bitno].type = AVR_CMDBIT_ADDRESS;
|
||||
op->bit[bitno].value = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
array_free(&array, n);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int avr_read_byte_default(PROGRAMMER * pgm, AVRPART * p, AVRMEM * mem,
|
||||
unsigned long addr, unsigned char * value)
|
||||
{
|
||||
unsigned char cmd[4];
|
||||
unsigned char res[4];
|
||||
unsigned char data;
|
||||
OPCODE * readop, * lext;
|
||||
|
||||
if (pgm->cmd == NULL) {
|
||||
logprintf(LOG_ERR, "%s programmer uses avr_read_byte_default() but does not provide a cmd() method", pgm->type);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* figure out what opcode to use
|
||||
*/
|
||||
if (mem->op[AVR_OP_READ_LO]) {
|
||||
if (addr & 0x00000001)
|
||||
readop = mem->op[AVR_OP_READ_HI];
|
||||
else
|
||||
readop = mem->op[AVR_OP_READ_LO];
|
||||
addr = addr / 2;
|
||||
}
|
||||
else {
|
||||
readop = mem->op[AVR_OP_READ];
|
||||
}
|
||||
|
||||
if (readop == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* If this device has a "load extended address" command, issue it.
|
||||
*/
|
||||
lext = mem->op[AVR_OP_LOAD_EXT_ADDR];
|
||||
|
||||
if (lext != NULL) {
|
||||
memset(cmd, 0, sizeof(cmd));
|
||||
|
||||
avr_set_bits(lext, cmd);
|
||||
avr_set_addr(lext, cmd, addr);
|
||||
pgm->cmd(pgm, cmd, res);
|
||||
}
|
||||
|
||||
memset(cmd, 0, sizeof(cmd));
|
||||
|
||||
avr_set_bits(readop, cmd);
|
||||
avr_set_addr(readop, cmd, addr);
|
||||
pgm->cmd(pgm, cmd, res);
|
||||
data = 0;
|
||||
avr_get_output(readop, res, &data);
|
||||
|
||||
*value = data;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Return the number of "interesting" bytes in a memory buffer,
|
||||
* "interesting" being defined as up to the last non-0xff data
|
||||
* value. This is useful for determining where to stop when dealing
|
||||
* with "flash" memory, since writing 0xff to flash is typically a
|
||||
* no-op. Always return an even number since flash is word addressed.
|
||||
*/
|
||||
int avr_mem_hiaddr(AVRMEM * mem)
|
||||
{
|
||||
int i, n;
|
||||
|
||||
/* return the highest non-0xff address regardless of how much
|
||||
memory was read */
|
||||
for (i=mem->size-1; i>0; i--) {
|
||||
if (mem->buf[i] != 0xff) {
|
||||
n = i+1;
|
||||
if (n & 0x01)
|
||||
return n+1;
|
||||
else
|
||||
return n;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Read the entirety of the specified memory type into the
|
||||
* corresponding buffer of the avrpart pointed to by 'p'. If size =
|
||||
* 0, read the entire contents, otherwise, read 'size' bytes.
|
||||
*
|
||||
* Return the number of bytes read, or < 0 if an error occurs.
|
||||
*/
|
||||
int avr_read(PROGRAMMER * pgm, AVRPART * p, char * memtype, int size)
|
||||
{
|
||||
int wsize;
|
||||
unsigned char rbyte;
|
||||
unsigned long i;
|
||||
unsigned char * buf;
|
||||
AVRMEM * mem = NULL;
|
||||
int rc;
|
||||
|
||||
/* NEW */
|
||||
if(strcmp(memtype, "signature") == 0) {
|
||||
mem = p->sigmem;
|
||||
} else if(strcmp(memtype, "flash") == 0) {
|
||||
mem = p->flashmem;
|
||||
} else if(strcmp(memtype, "lfuse") == 0) {
|
||||
mem = p->lfusemem;
|
||||
} else if(strcmp(memtype, "hfuse") == 0) {
|
||||
mem = p->hfusemem;
|
||||
}
|
||||
// mem = avr_locate_mem(p, memtype);
|
||||
if (mem == NULL) {
|
||||
logprintf(LOG_ERR, "No \"%s\" memory for part %s", memtype, p->desc);
|
||||
return -1;
|
||||
}
|
||||
|
||||
wsize = mem->size;
|
||||
if (size < wsize) {
|
||||
wsize = size;
|
||||
}
|
||||
|
||||
buf = mem->buf;
|
||||
if (size == 0) {
|
||||
size = mem->size;
|
||||
}
|
||||
|
||||
/*
|
||||
* start with all 0xff
|
||||
*/
|
||||
memset(buf, 0xff, size);
|
||||
|
||||
if (pgm->paged_load != NULL && mem->page_size != 0) {
|
||||
/*
|
||||
* the programmer supports a paged mode read
|
||||
*/
|
||||
int need_read, failure;
|
||||
unsigned int pageaddr;
|
||||
unsigned int npages, nread;
|
||||
|
||||
/* quickly scan number of pages to be written to first */
|
||||
for (pageaddr = 0, npages = 0;
|
||||
pageaddr < wsize;
|
||||
pageaddr += mem->page_size) {
|
||||
/* check whether this page must be read */
|
||||
for (i = pageaddr;
|
||||
i < pageaddr + mem->page_size;
|
||||
i++)
|
||||
if (i < mem->size) {
|
||||
npages++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (pageaddr = 0, failure = 0, nread = 0;
|
||||
!failure && pageaddr < wsize;
|
||||
pageaddr += mem->page_size) {
|
||||
/* check whether this page must be read */
|
||||
for (i = pageaddr, need_read = 0;
|
||||
i < pageaddr + mem->page_size;
|
||||
i++)
|
||||
if (i < mem->size) {
|
||||
need_read = 1;
|
||||
break;
|
||||
}
|
||||
if (need_read) {
|
||||
rc = pgm->paged_load(pgm, p, mem, mem->page_size,
|
||||
pageaddr, mem->page_size);
|
||||
if (rc < 0)
|
||||
/* paged load failed, fall back to byte-at-a-time read below */
|
||||
failure = 1;
|
||||
}
|
||||
nread++;
|
||||
report_progress(nread, npages, NULL);
|
||||
}
|
||||
if (!failure) {
|
||||
if (strcasecmp(mem->desc, "flash") == 0 ||
|
||||
strcasecmp(mem->desc, "application") == 0 ||
|
||||
strcasecmp(mem->desc, "apptable") == 0 ||
|
||||
strcasecmp(mem->desc, "boot") == 0)
|
||||
return avr_mem_hiaddr(mem);
|
||||
else
|
||||
return mem->size;
|
||||
}
|
||||
}
|
||||
|
||||
if (strcmp(mem->desc, "signature") == 0) {
|
||||
if (pgm->read_sig_bytes) {
|
||||
return pgm->read_sig_bytes(pgm, p, mem);
|
||||
}
|
||||
}
|
||||
|
||||
for (i=0; i<size; i++) {
|
||||
rc = pgm->read_byte(pgm, p, mem, i, &rbyte);
|
||||
if (rc != 0) {
|
||||
logprintf(LOG_ERR, "avr_read(): error reading address 0x%04lx", i);
|
||||
if (rc == -1)
|
||||
logprintf(LOG_ERR, "read operation not supported for memory \"%s\"", memtype);
|
||||
return -2;
|
||||
}
|
||||
buf[i] = rbyte;
|
||||
report_progress(i, size, NULL);
|
||||
}
|
||||
|
||||
if (strcasecmp(mem->desc, "flash") == 0)
|
||||
return avr_mem_hiaddr(mem);
|
||||
else
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* write a page data at the specified address
|
||||
*/
|
||||
int avr_write_page(PROGRAMMER * pgm, AVRPART * p, AVRMEM * mem,
|
||||
unsigned long addr)
|
||||
{
|
||||
unsigned char cmd[4];
|
||||
unsigned char res[4];
|
||||
OPCODE * wp, * lext;
|
||||
|
||||
if (pgm->cmd == NULL) {
|
||||
logprintf(LOG_ERR, "%s programmer uses avr_write_page() but does not provide a cmd() method", pgm->type);
|
||||
return -1;
|
||||
}
|
||||
|
||||
wp = mem->op[AVR_OP_WRITEPAGE];
|
||||
if (wp == NULL) {
|
||||
logprintf(LOG_ERR, "avr_write_page(): memory \"%s\" not configured for page writes", mem->desc);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* if this memory is word-addressable, adjust the address
|
||||
* accordingly
|
||||
*/
|
||||
if ((mem->op[AVR_OP_LOADPAGE_LO]) || (mem->op[AVR_OP_READ_LO]))
|
||||
addr = addr / 2;
|
||||
|
||||
/*
|
||||
* If this device has a "load extended address" command, issue it.
|
||||
*/
|
||||
lext = mem->op[AVR_OP_LOAD_EXT_ADDR];
|
||||
if (lext != NULL) {
|
||||
memset(cmd, 0, sizeof(cmd));
|
||||
|
||||
avr_set_bits(lext, cmd);
|
||||
avr_set_addr(lext, cmd, addr);
|
||||
pgm->cmd(pgm, cmd, res);
|
||||
}
|
||||
|
||||
memset(cmd, 0, sizeof(cmd));
|
||||
|
||||
avr_set_bits(wp, cmd);
|
||||
avr_set_addr(wp, cmd, addr);
|
||||
pgm->cmd(pgm, cmd, res);
|
||||
|
||||
/*
|
||||
* since we don't know what voltage the target AVR is powered by, be
|
||||
* conservative and delay the max amount the spec says to wait
|
||||
*/
|
||||
usleep(mem->max_write_delay);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int avr_write_byte_default(PROGRAMMER * pgm, AVRPART * p, AVRMEM * mem,
|
||||
unsigned long addr, unsigned char data)
|
||||
{
|
||||
unsigned char cmd[4];
|
||||
unsigned char res[4];
|
||||
unsigned char r;
|
||||
int ready;
|
||||
int tries;
|
||||
unsigned long start_time;
|
||||
unsigned long prog_time;
|
||||
unsigned char b;
|
||||
unsigned short caddr;
|
||||
OPCODE * writeop;
|
||||
int rc;
|
||||
int readok=0;
|
||||
struct timeval tv;
|
||||
|
||||
if (pgm->cmd == NULL) {
|
||||
logprintf(LOG_ERR, "%s programmer uses avr_write_byte_default() but does not provide a cmd() method.", pgm->type);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!mem->paged) {
|
||||
/*
|
||||
* check to see if the write is necessary by reading the existing
|
||||
* value and only write if we are changing the value; we can't
|
||||
* use this optimization for paged addressing.
|
||||
*/
|
||||
rc = pgm->read_byte(pgm, p, mem, addr, &b);
|
||||
if (rc != 0) {
|
||||
if (rc != -1) {
|
||||
return -2;
|
||||
}
|
||||
/*
|
||||
* the read operation is not support on this memory type
|
||||
*/
|
||||
}
|
||||
else {
|
||||
readok = 1;
|
||||
if (b == data) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* determine which memory opcode to use
|
||||
*/
|
||||
if (mem->op[AVR_OP_WRITE_LO]) {
|
||||
if (addr & 0x01)
|
||||
writeop = mem->op[AVR_OP_WRITE_HI];
|
||||
else
|
||||
writeop = mem->op[AVR_OP_WRITE_LO];
|
||||
caddr = addr / 2;
|
||||
}
|
||||
else if (mem->paged && mem->op[AVR_OP_LOADPAGE_LO]) {
|
||||
if (addr & 0x01)
|
||||
writeop = mem->op[AVR_OP_LOADPAGE_HI];
|
||||
else
|
||||
writeop = mem->op[AVR_OP_LOADPAGE_LO];
|
||||
caddr = addr / 2;
|
||||
}
|
||||
else {
|
||||
writeop = mem->op[AVR_OP_WRITE];
|
||||
caddr = addr;
|
||||
}
|
||||
|
||||
if (writeop == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(cmd, 0, sizeof(cmd));
|
||||
|
||||
avr_set_bits(writeop, cmd);
|
||||
avr_set_addr(writeop, cmd, caddr);
|
||||
avr_set_input(writeop, cmd, data);
|
||||
pgm->cmd(pgm, cmd, res);
|
||||
|
||||
if (mem->paged) {
|
||||
/*
|
||||
* in paged addressing, single bytes to be written to the memory
|
||||
* page complete immediately, we only need to delay when we commit
|
||||
* the whole page via the avr_write_page() routine.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (readok == 0) {
|
||||
/*
|
||||
* read operation not supported for this memory type, just wait
|
||||
* the max programming time and then return
|
||||
*/
|
||||
usleep(mem->max_write_delay); /* maximum write delay */
|
||||
return 0;
|
||||
}
|
||||
|
||||
tries = 0;
|
||||
ready = 0;
|
||||
while (!ready) {
|
||||
|
||||
if ((data == mem->readback[0]) ||
|
||||
(data == mem->readback[1])) {
|
||||
/*
|
||||
* use an extra long delay when we happen to be writing values
|
||||
* used for polled data read-back. In this case, polling
|
||||
* doesn't work, and we need to delay the worst case write time
|
||||
* specified for the chip.
|
||||
*/
|
||||
usleep(mem->max_write_delay);
|
||||
rc = pgm->read_byte(pgm, p, mem, addr, &r);
|
||||
if (rc != 0) {
|
||||
return -5;
|
||||
}
|
||||
}
|
||||
else {
|
||||
gettimeofday (&tv, NULL);
|
||||
start_time = (tv.tv_sec * 1000000) + tv.tv_usec;
|
||||
do {
|
||||
/*
|
||||
* Do polling, but timeout after max_write_delay.
|
||||
*/
|
||||
rc = pgm->read_byte(pgm, p, mem, addr, &r);
|
||||
if (rc != 0) {
|
||||
return -4;
|
||||
}
|
||||
gettimeofday (&tv, NULL);
|
||||
prog_time = (tv.tv_sec * 1000000) + tv.tv_usec;
|
||||
} while ((r != data) &&
|
||||
((prog_time-start_time) < mem->max_write_delay));
|
||||
}
|
||||
|
||||
/*
|
||||
* At this point we either have a valid readback or the
|
||||
* max_write_delay is expired.
|
||||
*/
|
||||
|
||||
if (r == data) {
|
||||
ready = 1;
|
||||
}
|
||||
else if (mem->pwroff_after_write) {
|
||||
/*
|
||||
* The device has been flagged as power-off after write to this
|
||||
* memory type. The reason we don't just blindly follow the
|
||||
* flag is that the power-off advice may only apply to some
|
||||
* memory bits but not all. We only actually power-off the
|
||||
* device if the data read back does not match what we wrote.
|
||||
*/
|
||||
logprintf(LOG_NOTICE, "this device must be powered off and back on to continue");
|
||||
if (pgm->pinno[PPI_AVR_VCC]) {
|
||||
logprintf(LOG_NOTICE, "attempting to do this now ...");
|
||||
pgm->powerdown(pgm);
|
||||
usleep(250000);
|
||||
rc = pgm->initialize(pgm, p);
|
||||
if (rc < 0) {
|
||||
logprintf(LOG_ERR, "initialization failed, rc=%d", rc);
|
||||
logprintf(LOG_ERR, "can't re-initialize device after programming the %s bits", mem->desc);
|
||||
return -3;
|
||||
}
|
||||
|
||||
logprintf(LOG_NOTICE, "device was successfully re-initialized");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
tries++;
|
||||
if (!ready && tries > 5) {
|
||||
/*
|
||||
* we wrote the data, but after waiting for what should have
|
||||
* been plenty of time, the memory cell still doesn't match what
|
||||
* we wrote. Indicate a write error.
|
||||
*/
|
||||
return -6;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* write a byte of data at the specified address
|
||||
*/
|
||||
int avr_write_byte(PROGRAMMER * pgm, AVRPART * p, AVRMEM * mem,
|
||||
unsigned long addr, unsigned char data)
|
||||
{
|
||||
|
||||
unsigned char safemode_lfuse;
|
||||
unsigned char safemode_hfuse;
|
||||
unsigned char safemode_efuse;
|
||||
unsigned char safemode_fuse;
|
||||
|
||||
/* If we write the fuses, then we need to tell safemode that they *should* change */
|
||||
safemode_memfuses(0, &safemode_lfuse, &safemode_hfuse, &safemode_efuse, &safemode_fuse);
|
||||
|
||||
if (strcmp(mem->desc, "fuse")==0) {
|
||||
safemode_fuse = data;
|
||||
}
|
||||
if (strcmp(mem->desc, "lfuse")==0) {
|
||||
safemode_lfuse = data;
|
||||
}
|
||||
if (strcmp(mem->desc, "hfuse")==0) {
|
||||
safemode_hfuse = data;
|
||||
}
|
||||
if (strcmp(mem->desc, "efuse")==0) {
|
||||
safemode_efuse = data;
|
||||
}
|
||||
|
||||
safemode_memfuses(1, &safemode_lfuse, &safemode_hfuse, &safemode_efuse, &safemode_fuse);
|
||||
|
||||
return pgm->write_byte(pgm, p, mem, addr, data);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Write the whole memory region of the specified memory from the
|
||||
* corresponding buffer of the avrpart pointed to by 'p'. Write up to
|
||||
* 'size' bytes from the buffer. Data is only written if the new data
|
||||
* value is different from the existing data value. Data beyond
|
||||
* 'size' bytes is not affected.
|
||||
*
|
||||
* Return the number of bytes written, or -1 if an error occurs.
|
||||
*/
|
||||
int avr_write(PROGRAMMER * pgm, AVRPART * p, char * memtype, int size)
|
||||
{
|
||||
int rc;
|
||||
int wsize;
|
||||
long i;
|
||||
unsigned char data;
|
||||
AVRMEM * m = NULL;
|
||||
|
||||
/* NEW */
|
||||
if(strcmp(memtype, "flash") == 0) {
|
||||
m = p->flashmem;
|
||||
} else if(strcmp(memtype, "lfuse") == 0) {
|
||||
m = p->lfusemem;
|
||||
} else if(strcmp(memtype, "hfuse") == 0) {
|
||||
m = p->hfusemem;
|
||||
}
|
||||
|
||||
if (m == NULL) {
|
||||
logprintf(LOG_ERR, "No \"%s\" memory for part %s", memtype, p->desc);
|
||||
return -1;
|
||||
}
|
||||
|
||||
wsize = m->size;
|
||||
if (size < wsize) {
|
||||
wsize = size;
|
||||
}
|
||||
else if (size > wsize) {
|
||||
logprintf(LOG_ERR, "%d bytes requested, but memory region is only %d bytes", size, wsize);
|
||||
logprintf(LOG_ERR, "Only %d bytes will actually be written", wsize);
|
||||
}
|
||||
|
||||
if (pgm->paged_write != NULL && m->page_size != 0) {
|
||||
/*
|
||||
* the programmer supports a paged mode write
|
||||
*/
|
||||
int need_write, failure;
|
||||
unsigned int pageaddr;
|
||||
unsigned int npages, nwritten;
|
||||
|
||||
/* quickly scan number of pages to be written to first */
|
||||
for (pageaddr = 0, npages = 0;
|
||||
pageaddr < wsize;
|
||||
pageaddr += m->page_size) {
|
||||
/* check whether this page must be written to */
|
||||
for (i = pageaddr;
|
||||
i < pageaddr + m->page_size;
|
||||
i++)
|
||||
if (i < m->size) {
|
||||
npages++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (pageaddr = 0, failure = 0, nwritten = 0;
|
||||
!failure && pageaddr < wsize;
|
||||
pageaddr += m->page_size) {
|
||||
/* check whether this page must be written to */
|
||||
for (i = pageaddr, need_write = 0;
|
||||
i < pageaddr + m->page_size;
|
||||
i++)
|
||||
if (i < m->size) {
|
||||
need_write = 1;
|
||||
break;
|
||||
}
|
||||
if (need_write) {
|
||||
rc = 0;
|
||||
if (rc >= 0)
|
||||
rc = pgm->paged_write(pgm, p, m, m->page_size, pageaddr, m->page_size);
|
||||
if (rc < 0)
|
||||
/* paged write failed, fall back to byte-at-a-time write below */
|
||||
failure = 1;
|
||||
}
|
||||
nwritten++;
|
||||
report_progress(nwritten, npages, NULL);
|
||||
}
|
||||
if (!failure)
|
||||
return wsize;
|
||||
/* else: fall back to byte-at-a-time write, for historical reasons */
|
||||
}
|
||||
|
||||
if (pgm->write_setup) {
|
||||
pgm->write_setup(pgm, p, m);
|
||||
}
|
||||
|
||||
for (i=0; i<wsize; i++) {
|
||||
data = m->buf[i];
|
||||
report_progress(i, wsize, NULL);
|
||||
|
||||
rc = avr_write_byte(pgm, p, m, i, data);
|
||||
if (rc) {
|
||||
logprintf(LOG_ERR, "***failed");
|
||||
}
|
||||
|
||||
if (m->paged) {
|
||||
/*
|
||||
* check to see if it is time to flush the page with a page
|
||||
* write
|
||||
*/
|
||||
if (((i % m->page_size) == m->page_size-1) ||
|
||||
(i == wsize-1)) {
|
||||
rc = avr_write_page(pgm, p, m, i);
|
||||
if (rc) {
|
||||
logprintf(LOG_ERR, " *** page %ld (addresses 0x%04lx - 0x%04lx) failed to write",
|
||||
i % m->page_size,
|
||||
i - m->page_size + 1, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* read the AVR device's signature bytes
|
||||
*/
|
||||
int avr_signature(PROGRAMMER * pgm, AVRPART * p)
|
||||
{
|
||||
int rc;
|
||||
|
||||
report_progress (0,1,"Reading");
|
||||
rc = avr_read(pgm, p, "signature", 0);
|
||||
if (rc < 0) {
|
||||
logprintf(LOG_ERR, "error reading signature data for part \"%s\", rc=%d", p->desc, rc);
|
||||
return -1;
|
||||
}
|
||||
report_progress (1,1,NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Verify the memory buffer of p with that of v. The byte range of v,
|
||||
* may be a subset of p. The byte range of p should cover the whole
|
||||
* chip's memory size.
|
||||
*
|
||||
* Return the number of bytes verified, or -1 if they don't match.
|
||||
*/
|
||||
int avr_verify(AVRPART * p, AVRPART * v, char * memtype, int size)
|
||||
{
|
||||
int i;
|
||||
unsigned char * buf1, * buf2;
|
||||
int vsize;
|
||||
AVRMEM * a = NULL, * b = NULL;
|
||||
|
||||
/* NEW */
|
||||
if(strcmp(memtype, "flash") == 0) {
|
||||
a = p->flashmem;
|
||||
} else if(strcmp(memtype, "lfuse") == 0) {
|
||||
a = p->lfusemem;
|
||||
} else if(strcmp(memtype, "hfuse") == 0) {
|
||||
a = p->hfusemem;
|
||||
}
|
||||
if (a == NULL) {
|
||||
logprintf(LOG_ERR, "No \"%s\" memory for part %s", memtype, p->desc);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* NEW */
|
||||
if(strcmp(memtype, "flash") == 0) {
|
||||
b = v->flashmem;
|
||||
} else if(strcmp(memtype, "lfuse") == 0) {
|
||||
b = v->lfusemem;
|
||||
} else if(strcmp(memtype, "hfuse") == 0) {
|
||||
b = v->hfusemem;
|
||||
}
|
||||
if (b == NULL) {
|
||||
logprintf(LOG_ERR, "No \"%s\" memory for part %s", memtype, p->desc);
|
||||
return -1;
|
||||
}
|
||||
|
||||
buf1 = a->buf;
|
||||
buf2 = b->buf;
|
||||
vsize = a->size;
|
||||
|
||||
if (vsize < size) {
|
||||
logprintf(LOG_ERR, "requested verification for %d bytes", size);
|
||||
logprintf(LOG_ERR, "%s memory region only contains %d bytes", memtype, vsize);
|
||||
logprintf(LOG_ERR, "Only %d bytes will be verified.", vsize);
|
||||
size = vsize;
|
||||
}
|
||||
|
||||
for (i=0; i<size; i++) {
|
||||
if (buf1[i] != buf2[i]) {
|
||||
logprintf(LOG_ERR, "verification error, first mismatch at byte 0x%04x", i);
|
||||
logprintf(LOG_ERR, "0x%02x != 0x%02x", buf1[i], buf2[i]);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
|
||||
int avr_get_cycle_count(PROGRAMMER * pgm, AVRPART * p, int * cycles)
|
||||
{
|
||||
AVRMEM * a = NULL;
|
||||
unsigned int cycle_count = 0;
|
||||
unsigned char v1;
|
||||
int rc;
|
||||
int i;
|
||||
|
||||
// a = avr_locate_mem(p, "eeprom");
|
||||
if (a == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (i=4; i>0; i--) {
|
||||
rc = pgm->read_byte(pgm, p, a, a->size-i, &v1);
|
||||
if (rc < 0) {
|
||||
logprintf(LOG_ERR, "can't read memory for cycle count, rc=%d", rc);
|
||||
return -1;
|
||||
}
|
||||
cycle_count = (cycle_count << 8) | v1;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the EEPROM is erased, the cycle count reads 0xffffffff.
|
||||
* In this case we return a cycle_count of zero.
|
||||
* So, the calling function don't have to care about whether or not
|
||||
* the cycle count was initialized.
|
||||
*/
|
||||
if (cycle_count == 0xffffffff) {
|
||||
cycle_count = 0;
|
||||
}
|
||||
|
||||
*cycles = (int) cycle_count;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int avr_put_cycle_count(PROGRAMMER * pgm, AVRPART * p, int cycles)
|
||||
{
|
||||
AVRMEM * a = NULL;
|
||||
unsigned char v1;
|
||||
int rc;
|
||||
int i;
|
||||
|
||||
// a = avr_locate_mem(p, "eeprom");
|
||||
if (a == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (i=1; i<=4; i++) {
|
||||
v1 = cycles & 0xff;
|
||||
cycles = cycles >> 8;
|
||||
|
||||
rc = avr_write_byte(pgm, p, a, a->size-i, v1);
|
||||
if (rc < 0) {
|
||||
logprintf(LOG_ERR, "can't write memory for cycle count, rc=%d", rc);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int avr_chip_erase(PROGRAMMER * pgm, AVRPART * p)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = pgm->chip_erase(pgm, p);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* Report the progress of a read or write operation from/to the
|
||||
* device.
|
||||
*
|
||||
* The first call of report_progress() should look like this (for a write op):
|
||||
*
|
||||
* report_progress (0, 1, "Writing");
|
||||
*
|
||||
* Then hdr should be passed NULL on subsequent calls while the
|
||||
* operation is progressing. Once the operation is complete, a final
|
||||
* call should be made as such to ensure proper termination of the
|
||||
* progress report:
|
||||
*
|
||||
* report_progress (1, 1, NULL);
|
||||
*
|
||||
* It would be nice if we could reduce the usage to one and only one
|
||||
* call for each of start, during and end cases. As things stand now,
|
||||
* that is not possible and makes maintenance a bit more work.
|
||||
*/
|
||||
void report_progress (int completed, int total, char *hdr)
|
||||
{
|
||||
static int last = 0;
|
||||
static double start_time;
|
||||
int percent = (completed * 100) / total;
|
||||
struct timeval tv;
|
||||
double t;
|
||||
|
||||
if (avr_update_progress == NULL)
|
||||
return;
|
||||
|
||||
gettimeofday(&tv, NULL);
|
||||
t = tv.tv_sec + ((double)tv.tv_usec)/1000000;
|
||||
|
||||
if (hdr) {
|
||||
last = 0;
|
||||
start_time = t;
|
||||
avr_update_progress (percent, t - start_time, hdr);
|
||||
}
|
||||
|
||||
if (percent > 100)
|
||||
percent = 100;
|
||||
|
||||
if (percent > last) {
|
||||
last = percent;
|
||||
avr_update_progress (percent, t - start_time, hdr);
|
||||
}
|
||||
|
||||
if (percent == 100)
|
||||
last = 0; /* Get ready for next time. */
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* avrdude - A Downloader/Uploader for AVR device programmers
|
||||
* Copyright (C) 2000-2004 Brian S. Dean <bsd@bsdhome.com>
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
/* $Id: avr.h 804 2009-02-23 22:04:57Z joerg_wunsch $ */
|
||||
|
||||
#ifndef __avr_h__
|
||||
#define __avr_h__
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "avrpart.h"
|
||||
#include "pgm.h"
|
||||
|
||||
typedef void (*FP_UpdateProgress)(int percent, double etime, char *hdr);
|
||||
|
||||
extern struct avrpart parts[];
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void avr_set_update_progress(FP_UpdateProgress callback);
|
||||
|
||||
int parse_cmdbits(OPCODE * op, char *t);
|
||||
|
||||
int avr_read_byte_default(PROGRAMMER * pgm, AVRPART * p, AVRMEM * mem,
|
||||
unsigned long addr, unsigned char * value);
|
||||
|
||||
int avr_read(PROGRAMMER * pgm, AVRPART * p, char * memtype, int size);
|
||||
|
||||
int avr_write_page(PROGRAMMER * pgm, AVRPART * p, AVRMEM * mem,
|
||||
unsigned long addr);
|
||||
|
||||
int avr_write_byte(PROGRAMMER * pgm, AVRPART * p, AVRMEM * mem,
|
||||
unsigned long addr, unsigned char data);
|
||||
|
||||
int avr_write_byte_default(PROGRAMMER * pgm, AVRPART * p, AVRMEM * mem,
|
||||
unsigned long addr, unsigned char data);
|
||||
|
||||
int avr_write(PROGRAMMER * pgm, AVRPART * p, char * memtype, int size);
|
||||
|
||||
int avr_signature(PROGRAMMER * pgm, AVRPART * p);
|
||||
|
||||
int avr_verify(AVRPART * p, AVRPART * v, char * memtype, int size);
|
||||
|
||||
int avr_get_cycle_count(PROGRAMMER * pgm, AVRPART * p, int * cycles);
|
||||
|
||||
int avr_put_cycle_count(PROGRAMMER * pgm, AVRPART * p, int cycles);
|
||||
|
||||
int avr_mem_hiaddr(AVRMEM * mem);
|
||||
|
||||
int avr_chip_erase(PROGRAMMER * pgm, AVRPART * p);
|
||||
|
||||
void report_progress (int completed, int total, char *hdr);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,341 @@
|
||||
/*
|
||||
* avrdude - A Downloader/Uploader for AVR device programmers
|
||||
* Copyright (C) 2000, 2001, 2002, 2003 Brian S. Dean <bsd@bsdhome.com>
|
||||
* Copyright (C) 2005 Michael Holzt <kju-avr@fqdn.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
/* $Id: bitbang.c 898 2010-01-08 20:02:35Z joerg_wunsch $ */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <signal.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include "../pilight/core/log.h"
|
||||
#include "../pilight/core/mem.h"
|
||||
#include "avrdude.h"
|
||||
#include "avr.h"
|
||||
#include "pindefs.h"
|
||||
#include "pgm.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
struct itimerval {
|
||||
struct timeval it_value, it_interval;
|
||||
};
|
||||
#define SIGALRM 14
|
||||
#define ITIMER_REAL 0
|
||||
#endif
|
||||
|
||||
static int delay_decrement;
|
||||
static volatile int done;
|
||||
|
||||
typedef void (*mysighandler_t)(int);
|
||||
static mysighandler_t saved_alarmhandler;
|
||||
|
||||
static void alarmhandler(int signo)
|
||||
{
|
||||
done = 1;
|
||||
signal(SIGALRM, saved_alarmhandler);
|
||||
}
|
||||
|
||||
/*
|
||||
* Calibrate the microsecond delay loop below.
|
||||
*/
|
||||
static void bitbang_calibrate_delay(void)
|
||||
{
|
||||
struct itimerval itv;
|
||||
volatile int i;
|
||||
|
||||
i = 0;
|
||||
done = 0;
|
||||
saved_alarmhandler = signal(SIGALRM, alarmhandler);
|
||||
/*
|
||||
* Set ITIMER_REAL to 100 ms. All known systems have a timer
|
||||
* granularity of 10 ms or better, so counting the delay cycles
|
||||
* accumulating over 100 ms should give us a rather realistic
|
||||
* picture, without annoying the user by a lengthy startup time (as
|
||||
* an alarm(1) would do). Of course, if heavy system activity
|
||||
* happens just during calibration but stops before the remaining
|
||||
* part of AVRDUDE runs, this will yield wrong values. There's not
|
||||
* much we can do about this.
|
||||
*/
|
||||
itv.it_value.tv_sec = 0;
|
||||
itv.it_value.tv_usec = 100000;
|
||||
itv.it_interval.tv_sec = itv.it_interval.tv_usec = 0;
|
||||
#ifndef _WIN32
|
||||
setitimer(ITIMER_REAL, &itv, 0);
|
||||
#endif
|
||||
while (!done)
|
||||
i--;
|
||||
itv.it_value.tv_sec = itv.it_value.tv_usec = 0;
|
||||
#ifndef _WIN32
|
||||
setitimer(ITIMER_REAL, &itv, 0);
|
||||
#endif
|
||||
/*
|
||||
* Calculate back from 100 ms to 1 us.
|
||||
*/
|
||||
delay_decrement = -i / 100000;
|
||||
}
|
||||
|
||||
/*
|
||||
* Delay for approximately the number of microseconds specified.
|
||||
* usleep()'s granularity is usually like 1 ms or 10 ms, so it's not
|
||||
* really suitable for short delays in bit-bang algorithms.
|
||||
*/
|
||||
void bitbang_delay(int us)
|
||||
{
|
||||
volatile int del = us * delay_decrement;
|
||||
|
||||
while (del > 0)
|
||||
del--;
|
||||
}
|
||||
|
||||
/*
|
||||
* transmit and receive a byte of data to/from the AVR device
|
||||
*/
|
||||
static unsigned char bitbang_txrx(PROGRAMMER * pgm, unsigned char byte)
|
||||
{
|
||||
int i;
|
||||
unsigned char r, b, rbyte;
|
||||
|
||||
rbyte = 0;
|
||||
for (i=7; i>=0; i--) {
|
||||
/*
|
||||
* Write and read one bit on SPI.
|
||||
* Some notes on timing: Let T be the time it takes to do
|
||||
* one pgm->setpin()-call resp. par clrpin()-call, then
|
||||
* - SCK is high for 2T
|
||||
* - SCK is low for 2T
|
||||
* - MOSI setuptime is 1T
|
||||
* - MOSI holdtime is 3T
|
||||
* - SCK low to MISO read is 2T to 3T
|
||||
* So we are within programming specs (expect for AT90S1200),
|
||||
* if and only if T>t_CLCL (t_CLCL=clock period of target system).
|
||||
*
|
||||
* Due to the delay introduced by "IN" and "OUT"-commands,
|
||||
* T is greater than 1us (more like 2us) on x86-architectures.
|
||||
* So programming works safely down to 1MHz target clock.
|
||||
*/
|
||||
|
||||
b = (byte >> i) & 0x01;
|
||||
|
||||
/* set the data input line as desired */
|
||||
pgm->setpin(pgm, pgm->pinno[PIN_AVR_MOSI], b);
|
||||
|
||||
pgm->setpin(pgm, pgm->pinno[PIN_AVR_SCK], 1);
|
||||
|
||||
/*
|
||||
* read the result bit (it is either valid from a previous falling
|
||||
* edge or it is ignored in the current context)
|
||||
*/
|
||||
r = pgm->getpin(pgm, pgm->pinno[PIN_AVR_MISO]);
|
||||
|
||||
pgm->setpin(pgm, pgm->pinno[PIN_AVR_SCK], 0);
|
||||
|
||||
rbyte |= r << i;
|
||||
}
|
||||
|
||||
return rbyte;
|
||||
}
|
||||
|
||||
|
||||
int bitbang_rdy_led(PROGRAMMER * pgm, int value)
|
||||
{
|
||||
// pgm->setpin(pgm, pgm->pinno[PIN_LED_RDY], !value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bitbang_err_led(PROGRAMMER * pgm, int value)
|
||||
{
|
||||
// pgm->setpin(pgm, pgm->pinno[PIN_LED_ERR], !value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bitbang_pgm_led(PROGRAMMER * pgm, int value)
|
||||
{
|
||||
// pgm->setpin(pgm, pgm->pinno[PIN_LED_PGM], !value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bitbang_vfy_led(PROGRAMMER * pgm, int value)
|
||||
{
|
||||
// pgm->setpin(pgm, pgm->pinno[PIN_LED_VFY], !value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* transmit an AVR device command and return the results; 'cmd' and
|
||||
* 'res' must point to at least a 4 byte data buffer
|
||||
*/
|
||||
int bitbang_cmd(PROGRAMMER * pgm, unsigned char cmd[4],
|
||||
unsigned char res[4])
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=0; i<4; i++) {
|
||||
res[i] = bitbang_txrx(pgm, cmd[i]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* transmit bytes via SPI and return the results; 'cmd' and
|
||||
* 'res' must point to data buffers
|
||||
*/
|
||||
int bitbang_spi(PROGRAMMER * pgm, unsigned char cmd[],
|
||||
unsigned char res[], int count)
|
||||
{
|
||||
int i;
|
||||
|
||||
pgm->setpin(pgm, pgm->pinno[PIN_LED_PGM], 0);
|
||||
|
||||
for (i=0; i<count; i++) {
|
||||
res[i] = bitbang_txrx(pgm, cmd[i]);
|
||||
}
|
||||
|
||||
pgm->setpin(pgm, pgm->pinno[PIN_LED_PGM], 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* issue the 'chip erase' command to the AVR device
|
||||
*/
|
||||
int bitbang_chip_erase(PROGRAMMER * pgm, AVRPART * p)
|
||||
{
|
||||
unsigned char cmd[4];
|
||||
unsigned char res[4];
|
||||
|
||||
if (p->op[AVR_OP_CHIP_ERASE] == NULL) {
|
||||
logprintf(LOG_ERR, "chip erase instruction not defined for part \"%s\"", p->desc);
|
||||
return -1;
|
||||
}
|
||||
|
||||
pgm->pgm_led(pgm, ON);
|
||||
|
||||
memset(cmd, 0, sizeof(cmd));
|
||||
|
||||
avr_set_bits(p->op[AVR_OP_CHIP_ERASE], cmd);
|
||||
pgm->cmd(pgm, cmd, res);
|
||||
usleep(p->chip_erase_delay);
|
||||
pgm->initialize(pgm, p);
|
||||
|
||||
pgm->pgm_led(pgm, OFF);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* issue the 'program enable' command to the AVR device
|
||||
*/
|
||||
int bitbang_program_enable(PROGRAMMER * pgm, AVRPART * p)
|
||||
{
|
||||
unsigned char cmd[4];
|
||||
unsigned char res[4];
|
||||
|
||||
if (p->op[AVR_OP_PGM_ENABLE] == NULL) {
|
||||
logprintf(LOG_ERR, "program enable instruction not defined for part \"%s\"", p->desc);
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(cmd, 0, sizeof(cmd));
|
||||
avr_set_bits(p->op[AVR_OP_PGM_ENABLE], cmd);
|
||||
pgm->cmd(pgm, cmd, res);
|
||||
|
||||
if (res[2] != cmd[1])
|
||||
return -2;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* initialize the AVR device and prepare it to accept commands
|
||||
*/
|
||||
int bitbang_initialize(PROGRAMMER * pgm, AVRPART * p)
|
||||
{
|
||||
int rc;
|
||||
int tries;
|
||||
|
||||
bitbang_calibrate_delay();
|
||||
|
||||
pgm->powerup(pgm);
|
||||
usleep(20000);
|
||||
|
||||
pgm->setpin(pgm, pgm->pinno[PIN_AVR_SCK], 0);
|
||||
pgm->setpin(pgm, pgm->pinno[PIN_AVR_RESET], 0);
|
||||
usleep(20000);
|
||||
|
||||
pgm->highpulsepin(pgm, pgm->pinno[PIN_AVR_RESET]);
|
||||
|
||||
usleep(20000); /* 20 ms XXX should be a per-chip parameter */
|
||||
|
||||
/*
|
||||
* Enable programming mode. If we are programming an AT90S1200, we
|
||||
* can only issue the command and hope it worked. If we are using
|
||||
* one of the other chips, the chip will echo 0x53 when issuing the
|
||||
* third byte of the command. In this case, try up to 32 times in
|
||||
* order to possibly get back into sync with the chip if we are out
|
||||
* of sync.
|
||||
*/
|
||||
|
||||
if (strcmp(p->desc, "AT90S1200")==0) {
|
||||
pgm->program_enable(pgm, p);
|
||||
}
|
||||
else {
|
||||
tries = 0;
|
||||
do {
|
||||
rc = pgm->program_enable(pgm, p);
|
||||
if ((rc == 0)||(rc == -1))
|
||||
break;
|
||||
pgm->highpulsepin(pgm, pgm->pinno[p->retry_pulse/*PIN_AVR_SCK*/]);
|
||||
tries++;
|
||||
} while (tries < 65);
|
||||
|
||||
/*
|
||||
* can't sync with the device, maybe it's not attached?
|
||||
*/
|
||||
if (rc) {
|
||||
logprintf(LOG_ERR, "AVR device not responding");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void verify_pin_assigned(PROGRAMMER * pgm, int pin, char * desc)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Verify all prerequisites for a bit-bang programmer are present.
|
||||
*/
|
||||
void bitbang_check_prerequisites(PROGRAMMER *pgm)
|
||||
{
|
||||
verify_pin_assigned(pgm, PIN_AVR_RESET, "AVR RESET");
|
||||
verify_pin_assigned(pgm, PIN_AVR_SCK, "AVR SCK");
|
||||
verify_pin_assigned(pgm, PIN_AVR_MISO, "AVR MISO");
|
||||
verify_pin_assigned(pgm, PIN_AVR_MOSI, "AVR MOSI");
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* avrdude - A Downloader/Uploader for AVR device programmers
|
||||
* Copyright (C) 2000, 2001, 2002, 2003 Brian S. Dean <bsd@bsdhome.com>
|
||||
* Copyright (C) 2005 Michael Holzt <kju-avr@fqdn.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
/* $Id: bitbang.h 797 2009-02-17 15:31:27Z joerg_wunsch $ */
|
||||
|
||||
#ifndef avrbitbang_h
|
||||
#define avrbitbang_h
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
int bitbang_setpin(int fd, int pin, int value);
|
||||
int bitbang_getpin(int fd, int pin);
|
||||
int bitbang_highpulsepin(int fd, int pin);
|
||||
void bitbang_delay(unsigned int us);
|
||||
|
||||
void bitbang_check_prerequisites(PROGRAMMER *pgm);
|
||||
|
||||
int bitbang_rdy_led (PROGRAMMER * pgm, int value);
|
||||
int bitbang_err_led (PROGRAMMER * pgm, int value);
|
||||
int bitbang_pgm_led (PROGRAMMER * pgm, int value);
|
||||
int bitbang_vfy_led (PROGRAMMER * pgm, int value);
|
||||
int bitbang_cmd (PROGRAMMER * pgm, unsigned char cmd[4],
|
||||
unsigned char res[4]);
|
||||
int bitbang_spi (PROGRAMMER * pgm, unsigned char cmd[],
|
||||
unsigned char res[], int count);
|
||||
int bitbang_chip_erase (PROGRAMMER * pgm, AVRPART * p);
|
||||
int bitbang_program_enable (PROGRAMMER * pgm, AVRPART * p);
|
||||
void bitbang_powerup (PROGRAMMER * pgm);
|
||||
void bitbang_powerdown (PROGRAMMER * pgm);
|
||||
int bitbang_initialize (PROGRAMMER * pgm, AVRPART * p);
|
||||
void bitbang_disable (PROGRAMMER * pgm);
|
||||
void bitbang_enable (PROGRAMMER * pgm);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* avrdude - A Downloader/Uploader for AVR device programmers
|
||||
* Copyright (C) 2000-2004 Brian S. Dean <bsd@bsdhome.com>
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
/* $Id: config.c 725 2007-01-30 13:41:54Z joerg_wunsch $ */
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "../pilight/core/log.h"
|
||||
#include "../pilight/core/mem.h"
|
||||
#include "avrconfig.h"
|
||||
|
||||
TOKEN * new_token(int primary)
|
||||
{
|
||||
TOKEN * tkn;
|
||||
|
||||
tkn = (TOKEN *)MALLOC(sizeof(TOKEN));
|
||||
if (tkn == NULL) {
|
||||
fprintf(stderr, "out of memory\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
memset(tkn, 0, sizeof(TOKEN));
|
||||
|
||||
tkn->primary = primary;
|
||||
|
||||
return tkn;
|
||||
}
|
||||
|
||||
void free_token(TOKEN * tkn)
|
||||
{
|
||||
if (tkn) {
|
||||
switch (tkn->primary) {
|
||||
case TKN_STRING:
|
||||
case TKN_ID:
|
||||
if (tkn->value.string)
|
||||
FREE(tkn->value.string);
|
||||
tkn->value.string = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
FREE(tkn);
|
||||
}
|
||||
}
|
||||
|
||||
TOKEN * number(char * text)
|
||||
{
|
||||
struct token_t * tkn;
|
||||
|
||||
tkn = new_token(TKN_NUMBER);
|
||||
tkn->value.type = V_NUM;
|
||||
tkn->value.number = atof(text);
|
||||
|
||||
return tkn;
|
||||
}
|
||||
|
||||
TOKEN * hexnumber(char * text)
|
||||
{
|
||||
struct token_t * tkn;
|
||||
char * e;
|
||||
|
||||
tkn = new_token(TKN_NUMBER);
|
||||
tkn->value.type = V_NUM;
|
||||
tkn->value.number = strtoul(text, &e, 16);
|
||||
if ((e == text) || (*e != 0)) {
|
||||
logprintf(LOG_ERR, "%s:%d: can't scan hex number \"%s\"", infile, lineno, text);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return tkn;
|
||||
}
|
||||
|
||||
TOKEN * string(char * text)
|
||||
{
|
||||
struct token_t * tkn;
|
||||
int len;
|
||||
|
||||
tkn = new_token(TKN_STRING);
|
||||
|
||||
len = strlen(text);
|
||||
|
||||
tkn->value.type = V_STR;
|
||||
tkn->value.string = (char *) MALLOC(len+1);
|
||||
if (tkn->value.string == NULL) {
|
||||
fprintf(stderr, "out of memory\n");
|
||||
return NULL;
|
||||
}
|
||||
strcpy(tkn->value.string, text);
|
||||
|
||||
return tkn;
|
||||
}
|
||||
@@ -0,0 +1,237 @@
|
||||
/*
|
||||
* avrdude - A Downloader/Uploader for AVR device programmers
|
||||
* Copyright (C) 2000-2004 Brian S. Dean <bsd@bsdhome.com>
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
/* $Id: config.h 725 2007-01-30 13:41:54Z joerg_wunsch $ */
|
||||
|
||||
#ifndef avrconfig_h
|
||||
#define avrconfig_h
|
||||
|
||||
#include "avrpart.h"
|
||||
#include "pindefs.h"
|
||||
#include "avr.h"
|
||||
#include "pgm.h"
|
||||
|
||||
#define MAX_STR_CONST 1024
|
||||
|
||||
enum { V_NONE, V_NUM, V_STR };
|
||||
typedef struct value_t {
|
||||
int type;
|
||||
double number;
|
||||
char * string;
|
||||
} VALUE;
|
||||
|
||||
|
||||
typedef struct token_t {
|
||||
int primary;
|
||||
VALUE value;
|
||||
} TOKEN;
|
||||
typedef struct token_t *token_p;
|
||||
|
||||
|
||||
extern FILE * in;
|
||||
extern PROGRAMMER * current_prog;
|
||||
extern AVRPART * current_part;
|
||||
extern AVRMEM * current_mem;
|
||||
extern int lineno;
|
||||
extern const char * infile;
|
||||
extern char default_programmer[];
|
||||
extern char default_parallel[];
|
||||
extern char default_serial[];
|
||||
|
||||
enum tokentype {
|
||||
K_READ = 258,
|
||||
K_WRITE = 259,
|
||||
K_READ_LO = 260,
|
||||
K_READ_HI = 261,
|
||||
K_WRITE_LO = 262,
|
||||
K_WRITE_HI = 263,
|
||||
K_LOADPAGE_LO = 264,
|
||||
K_LOADPAGE_HI = 265,
|
||||
K_LOAD_EXT_ADDR = 266,
|
||||
K_WRITEPAGE = 267,
|
||||
K_CHIP_ERASE = 268,
|
||||
K_PGM_ENABLE = 269,
|
||||
K_MEMORY = 270,
|
||||
K_PAGE_SIZE = 271,
|
||||
K_PAGED = 272,
|
||||
K_ARDUINO = 273,
|
||||
K_BAUDRATE = 274,
|
||||
K_BS2 = 275,
|
||||
K_BUFF = 276,
|
||||
K_BUSPIRATE = 277,
|
||||
K_CHIP_ERASE_DELAY = 278,
|
||||
K_DEDICATED = 279,
|
||||
K_DEFAULT_PARALLEL = 280,
|
||||
K_DEFAULT_PROGRAMMER = 281,
|
||||
K_DEFAULT_SERIAL = 282,
|
||||
K_DESC = 283,
|
||||
K_DEVICECODE = 284,
|
||||
K_DRAGON_DW = 285,
|
||||
K_DRAGON_HVSP = 286,
|
||||
K_DRAGON_ISP = 287,
|
||||
K_DRAGON_JTAG = 288,
|
||||
K_DRAGON_PDI = 289,
|
||||
K_DRAGON_PP = 290,
|
||||
K_GPIO = 291,
|
||||
K_STK500_DEVCODE = 292,
|
||||
K_AVR910_DEVCODE = 293,
|
||||
K_EEPROM = 294,
|
||||
K_ERRLED = 295,
|
||||
K_FLASH = 296,
|
||||
K_ID = 297,
|
||||
K_IO = 298,
|
||||
K_JTAG_MKI = 299,
|
||||
K_JTAG_MKII = 300,
|
||||
K_JTAG_MKII_AVR32 = 301,
|
||||
K_JTAG_MKII_DW = 302,
|
||||
K_JTAG_MKII_ISP = 303,
|
||||
K_JTAG_MKII_PDI = 304,
|
||||
K_LOADPAGE = 305,
|
||||
K_MAX_WRITE_DELAY = 306,
|
||||
K_MIN_WRITE_DELAY = 307,
|
||||
K_MISO = 308,
|
||||
K_MOSI = 309,
|
||||
K_NUM_PAGES = 310,
|
||||
K_NVM_BASE = 311,
|
||||
K_OFFSET = 312,
|
||||
K_PAGEL = 313,
|
||||
K_PAR = 314,
|
||||
K_PARALLEL = 315,
|
||||
K_PART = 316,
|
||||
K_PGMLED = 317,
|
||||
K_PROGRAMMER = 318,
|
||||
K_PSEUDO = 319,
|
||||
K_PWROFF_AFTER_WRITE = 320,
|
||||
K_RDYLED = 321,
|
||||
K_READBACK_P1 = 322,
|
||||
K_READBACK_P2 = 323,
|
||||
K_READMEM = 324,
|
||||
K_RESET = 325,
|
||||
K_RETRY_PULSE = 326,
|
||||
K_SERBB = 327,
|
||||
K_SERIAL = 328,
|
||||
K_SCK = 329,
|
||||
K_SIGNATURE = 330,
|
||||
K_SIZE = 331,
|
||||
K_STK500 = 332,
|
||||
K_STK500HVSP = 333,
|
||||
K_STK500PP = 334,
|
||||
K_STK500V2 = 335,
|
||||
K_STK500GENERIC = 336,
|
||||
K_STK600 = 337,
|
||||
K_STK600HVSP = 338,
|
||||
K_STK600PP = 339,
|
||||
K_AVR910 = 340,
|
||||
K_USBASP = 341,
|
||||
K_USBTINY = 342,
|
||||
K_BUTTERFLY = 343,
|
||||
K_TYPE = 344,
|
||||
K_VCC = 345,
|
||||
K_VFYLED = 346,
|
||||
K_NO = 347,
|
||||
K_YES = 348,
|
||||
K_TIMEOUT = 349,
|
||||
K_STABDELAY = 350,
|
||||
K_CMDEXEDELAY = 351,
|
||||
K_HVSPCMDEXEDELAY = 352,
|
||||
K_SYNCHLOOPS = 353,
|
||||
K_BYTEDELAY = 354,
|
||||
K_POLLVALUE = 355,
|
||||
K_POLLINDEX = 356,
|
||||
K_PREDELAY = 357,
|
||||
K_POSTDELAY = 358,
|
||||
K_POLLMETHOD = 359,
|
||||
K_MODE = 360,
|
||||
K_DELAY = 361,
|
||||
K_BLOCKSIZE = 362,
|
||||
K_READSIZE = 363,
|
||||
K_HVENTERSTABDELAY = 364,
|
||||
K_PROGMODEDELAY = 365,
|
||||
K_LATCHCYCLES = 366,
|
||||
K_TOGGLEVTG = 367,
|
||||
K_POWEROFFDELAY = 368,
|
||||
K_RESETDELAYMS = 369,
|
||||
K_RESETDELAYUS = 370,
|
||||
K_HVLEAVESTABDELAY = 371,
|
||||
K_RESETDELAY = 372,
|
||||
K_SYNCHCYCLES = 373,
|
||||
K_HVCMDEXEDELAY = 374,
|
||||
K_CHIPERASEPULSEWIDTH = 375,
|
||||
K_CHIPERASEPOLLTIMEOUT = 376,
|
||||
K_CHIPERASETIME = 377,
|
||||
K_PROGRAMFUSEPULSEWIDTH = 378,
|
||||
K_PROGRAMFUSEPOLLTIMEOUT = 379,
|
||||
K_PROGRAMLOCKPULSEWIDTH = 380,
|
||||
K_PROGRAMLOCKPOLLTIMEOUT = 381,
|
||||
K_PP_CONTROLSTACK = 382,
|
||||
K_HVSP_CONTROLSTACK = 383,
|
||||
K_ALLOWFULLPAGEBITSTREAM = 384,
|
||||
K_ENABLEPAGEPROGRAMMING = 385,
|
||||
K_HAS_JTAG = 386,
|
||||
K_HAS_DW = 387,
|
||||
K_HAS_PDI = 388,
|
||||
K_HAS_TPI = 389,
|
||||
K_IDR = 390,
|
||||
K_IS_AVR32 = 391,
|
||||
K_RAMPZ = 392,
|
||||
K_SPMCR = 393,
|
||||
K_EECR = 394,
|
||||
K_FLASH_INSTR = 395,
|
||||
K_EEPROM_INSTR = 396,
|
||||
TKN_COMMA = 397,
|
||||
TKN_EQUAL = 398,
|
||||
TKN_SEMI = 399,
|
||||
TKN_TILDE = 400,
|
||||
TKN_NUMBER = 401,
|
||||
TKN_STRING = 402,
|
||||
TKN_ID = 403
|
||||
};
|
||||
|
||||
char default_programmer[MAX_STR_CONST];
|
||||
char default_parallel[4096];
|
||||
char default_serial[4096];
|
||||
|
||||
char string_buf[MAX_STR_CONST];
|
||||
char *string_buf_ptr;
|
||||
|
||||
PROGRAMMER * current_prog;
|
||||
AVRPART * current_part;
|
||||
|
||||
int lineno;
|
||||
const char * infile;
|
||||
extern char string_buf[MAX_STR_CONST];
|
||||
extern char *string_buf_ptr;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
int init_config(void);
|
||||
TOKEN * new_token(int primary);
|
||||
void free_token(TOKEN * tkn);
|
||||
void free_tokens(int n, ...);
|
||||
TOKEN * number(char * text);
|
||||
TOKEN * hexnumber(char * text);
|
||||
TOKEN * string(char * text);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* avrdude - A Downloader/Uploader for AVR device programmers
|
||||
* Copyright (C) 2007 Joerg Wunsch
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
/* $Id: avrdude.h 788 2008-11-19 23:32:30Z joerg_wunsch $ */
|
||||
|
||||
#ifndef avrdude_h
|
||||
#define avrdude_h
|
||||
|
||||
extern char * progname; /* name of program, for messages */
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,177 @@
|
||||
/*
|
||||
* avrdude - A Downloader/Uploader for AVR device programmers
|
||||
* Support for bitbanging GPIO pins using the /sys/class/gpio interface
|
||||
*
|
||||
* Copyright (C) 2010 Radoslav Kolev <radoslav@kolev.info>
|
||||
*
|
||||
* 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <wiringx.h>
|
||||
|
||||
#include "avrdude.h"
|
||||
#include "avr.h"
|
||||
#include "pindefs.h"
|
||||
#include "pgm.h"
|
||||
#include "avrbitbang.h"
|
||||
#include "defines.h"
|
||||
#include "../pilight/core/log.h"
|
||||
#include "../pilight/config/settings.h"
|
||||
|
||||
/*
|
||||
* GPIO user space helpers
|
||||
*
|
||||
* Copyright 2009 Analog Devices Inc.
|
||||
* Michael Hennerich (hennerich@blackfin.uclinux.org)
|
||||
*
|
||||
* Licensed under the GPL-2 or later
|
||||
*/
|
||||
|
||||
#define GPIO_DIR_IN 0
|
||||
#define GPIO_DIR_OUT 1
|
||||
#define N_GPIO 256
|
||||
|
||||
/*
|
||||
* an array which holds open FDs to /sys/class/gpio/gpioXX/value for all needed pins
|
||||
*/
|
||||
static int gpio_fds[N_GPIO];
|
||||
|
||||
static int gpio_setpin(PROGRAMMER * pgm, int pin, int value) {
|
||||
if(gpio_fds[pin] != PINMODE_OUTPUT) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Small delay for too fast computers */
|
||||
delayMicroseconds(1);
|
||||
|
||||
if(value == 1) {
|
||||
digitalWrite(pin, HIGH);
|
||||
} else {
|
||||
digitalWrite(pin, LOW);
|
||||
}
|
||||
|
||||
if(pgm->ispdelay > 1) {
|
||||
bitbang_delay(pgm->ispdelay);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gpio_getpin(PROGRAMMER * pgm, int pin) {
|
||||
if(gpio_fds[pin] == PINMODE_INPUT) {
|
||||
return digitalRead(pin);
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static int gpio_highpulsepin(PROGRAMMER * pgm, int pin) {
|
||||
|
||||
if(gpio_fds[pin] == PINMODE_OUTPUT) {
|
||||
digitalWrite(pin, HIGH);
|
||||
digitalWrite(pin, LOW);
|
||||
return 0;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static void gpio_display(PROGRAMMER *pgm, const char *p) {
|
||||
/* MAYBE */
|
||||
}
|
||||
|
||||
static void gpio_enable(PROGRAMMER *pgm) {
|
||||
/* nothing */
|
||||
}
|
||||
|
||||
static void gpio_disable(PROGRAMMER *pgm) {
|
||||
/* nothing */
|
||||
}
|
||||
|
||||
static void gpio_powerup(PROGRAMMER *pgm) {
|
||||
/* nothing */
|
||||
}
|
||||
|
||||
static void gpio_powerdown(PROGRAMMER *pgm) {
|
||||
/* nothing */
|
||||
}
|
||||
|
||||
static int gpio_open(PROGRAMMER *pgm, char *port) {
|
||||
int i = 0;
|
||||
|
||||
bitbang_check_prerequisites(pgm);
|
||||
|
||||
for(i=0;i<N_PINS;i++) {
|
||||
if(pgm->pinno[i] != 0) {
|
||||
if(i == PIN_AVR_MISO) {
|
||||
gpio_fds[pgm->pinno[i]] = PINMODE_INPUT;
|
||||
pinMode(pgm->pinno[i], PINMODE_INPUT);
|
||||
} else {
|
||||
gpio_fds[pgm->pinno[i]] = PINMODE_OUTPUT;;
|
||||
pinMode(pgm->pinno[i], PINMODE_OUTPUT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
static void gpio_close(PROGRAMMER *pgm) {
|
||||
if(gpio_fds[pgm->pinno[PIN_AVR_RESET]] == PINMODE_OUTPUT) {
|
||||
digitalWrite(pgm->pinno[PIN_AVR_RESET], HIGH);
|
||||
// digitalWrite(pgm->pinno[PIN_AVR_RESET], LOW);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void gpio_initpgm(PROGRAMMER *pgm)
|
||||
{
|
||||
strcpy(pgm->type, "GPIO");
|
||||
char *platform = GPIO_PLATFORM;
|
||||
if(settings_find_string("gpio-platform", &platform) != 0 || strcmp(platform, "none") == 0) {
|
||||
logprintf(LOG_ERR, "gpio_switch: no gpio-platform configured");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if(wiringXSetup(platform, logprintf1) < 0) {
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
pgm->rdy_led = bitbang_rdy_led;
|
||||
pgm->err_led = bitbang_err_led;
|
||||
pgm->pgm_led = bitbang_pgm_led;
|
||||
pgm->vfy_led = bitbang_vfy_led;
|
||||
pgm->initialize = bitbang_initialize;
|
||||
pgm->display = gpio_display;
|
||||
pgm->enable = gpio_enable;
|
||||
pgm->disable = gpio_disable;
|
||||
pgm->powerup = gpio_powerup;
|
||||
pgm->powerdown = gpio_powerdown;
|
||||
pgm->program_enable = bitbang_program_enable;
|
||||
pgm->chip_erase = bitbang_chip_erase;
|
||||
pgm->cmd = bitbang_cmd;
|
||||
pgm->open = gpio_open;
|
||||
pgm->close = gpio_close;
|
||||
pgm->setpin = gpio_setpin;
|
||||
pgm->getpin = gpio_getpin;
|
||||
pgm->highpulsepin = gpio_highpulsepin;
|
||||
pgm->read_byte = avr_read_byte_default;
|
||||
pgm->write_byte = avr_write_byte_default;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* avrdude - A Downloader/Uploader for AVR device programmers
|
||||
* Copyright (C) 2000-2004 Brian S. Dean <bsd@bsdhome.com>
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
/* $Id: par.h 722 2007-01-24 22:43:46Z joerg_wunsch $ */
|
||||
|
||||
#ifndef avrgpio_h
|
||||
#define avrgpio_h
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void gpio_initpgm (PROGRAMMER * pgm);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,251 @@
|
||||
|
||||
/*
|
||||
* avrdude - A Downloader/Uploader for AVR device programmers
|
||||
* Copyright (C) 2000-2004 Brian S. Dean <bsd@bsdhome.com>
|
||||
* Copyright (C) 2006 Joerg Wunsch <j@uriah.heep.sax.de>
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
/* $Id: avrpart.c 772 2008-06-07 21:03:41Z joerg_wunsch $ */
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "../pilight/core/log.h"
|
||||
#include "../pilight/core/mem.h"
|
||||
#include "avrdude.h"
|
||||
#include "avrpart.h"
|
||||
#include "pindefs.h"
|
||||
|
||||
/***
|
||||
*** Elementary functions dealing with OPCODE structures
|
||||
***/
|
||||
|
||||
OPCODE * avr_new_opcode(void)
|
||||
{
|
||||
OPCODE * m;
|
||||
|
||||
m = (OPCODE *)MALLOC(sizeof(*m));
|
||||
if (m == NULL) {
|
||||
fprintf(stderr, "out of memory\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
memset(m, 0, sizeof(*m));
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* avr_set_bits()
|
||||
*
|
||||
* Set instruction bits in the specified command based on the opcode.
|
||||
*/
|
||||
int avr_set_bits(OPCODE * op, unsigned char * cmd)
|
||||
{
|
||||
int i, j, bit;
|
||||
unsigned char mask;
|
||||
|
||||
for (i=0; i<32; i++) {
|
||||
if (op->bit[i].type == AVR_CMDBIT_VALUE) {
|
||||
j = 3 - i / 8;
|
||||
bit = i % 8;
|
||||
mask = 1 << bit;
|
||||
if (op->bit[i].value)
|
||||
cmd[j] = cmd[j] | mask;
|
||||
else
|
||||
cmd[j] = cmd[j] & ~mask;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* avr_set_addr()
|
||||
*
|
||||
* Set address bits in the specified command based on the opcode, and
|
||||
* the address.
|
||||
*/
|
||||
int avr_set_addr(OPCODE * op, unsigned char * cmd, unsigned long addr)
|
||||
{
|
||||
int i, j, bit;
|
||||
unsigned long value;
|
||||
unsigned char mask;
|
||||
|
||||
for (i=0; i<32; i++) {
|
||||
if (op->bit[i].type == AVR_CMDBIT_ADDRESS) {
|
||||
j = 3 - i / 8;
|
||||
bit = i % 8;
|
||||
mask = 1 << bit;
|
||||
value = addr >> op->bit[i].bitno & 0x01;
|
||||
if (value)
|
||||
cmd[j] = cmd[j] | mask;
|
||||
else
|
||||
cmd[j] = cmd[j] & ~mask;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* avr_set_input()
|
||||
*
|
||||
* Set input data bits in the specified command based on the opcode,
|
||||
* and the data byte.
|
||||
*/
|
||||
int avr_set_input(OPCODE * op, unsigned char * cmd, unsigned char data)
|
||||
{
|
||||
int i, j, bit;
|
||||
unsigned char value;
|
||||
unsigned char mask;
|
||||
|
||||
for (i=0; i<32; i++) {
|
||||
if (op->bit[i].type == AVR_CMDBIT_INPUT) {
|
||||
j = 3 - i / 8;
|
||||
bit = i % 8;
|
||||
mask = 1 << bit;
|
||||
value = data >> op->bit[i].bitno & 0x01;
|
||||
if (value)
|
||||
cmd[j] = cmd[j] | mask;
|
||||
else
|
||||
cmd[j] = cmd[j] & ~mask;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* avr_get_output()
|
||||
*
|
||||
* Retreive output data bits from the command results based on the
|
||||
* opcode data.
|
||||
*/
|
||||
int avr_get_output(OPCODE * op, unsigned char * res, unsigned char * data)
|
||||
{
|
||||
int i, j, bit;
|
||||
unsigned char value;
|
||||
unsigned char mask;
|
||||
|
||||
for (i=0; i<32; i++) {
|
||||
if (op->bit[i].type == AVR_CMDBIT_OUTPUT) {
|
||||
j = 3 - i / 8;
|
||||
bit = i % 8;
|
||||
mask = 1 << bit;
|
||||
value = ((res[j] & mask) >> bit) & 0x01;
|
||||
value = value << op->bit[i].bitno;
|
||||
if (value)
|
||||
*data = *data | value;
|
||||
else
|
||||
*data = *data & ~value;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/***
|
||||
*** Elementary functions dealing with AVRMEM structures
|
||||
***/
|
||||
|
||||
AVRMEM * avr_new_memtype(void)
|
||||
{
|
||||
AVRMEM * m;
|
||||
|
||||
m = (AVRMEM *)MALLOC(sizeof(*m));
|
||||
if (m == NULL) {
|
||||
fprintf(stderr, "out of memory\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
memset(m, 0, sizeof(*m));
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
|
||||
AVRMEM * avr_dup_mem(AVRMEM * m)
|
||||
{
|
||||
AVRMEM * n;
|
||||
|
||||
n = avr_new_memtype();
|
||||
|
||||
*n = *m;
|
||||
|
||||
n->buf = (unsigned char *)MALLOC(n->size);
|
||||
if (n->buf == NULL) {
|
||||
fprintf(stderr, "out of memory\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
memset(n->buf, 0, n->size);
|
||||
|
||||
memcpy(n->tags, m->tags, n->size);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
/*
|
||||
* Elementary functions dealing with AVRPART structures
|
||||
*/
|
||||
|
||||
|
||||
AVRPART * avr_new_part(void)
|
||||
{
|
||||
AVRPART * p;
|
||||
|
||||
p = (AVRPART *)MALLOC(sizeof(AVRPART));
|
||||
if (p == NULL) {
|
||||
fprintf(stderr, "out of memory\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
memset(p, 0, sizeof(*p));
|
||||
|
||||
p->id[0] = 0;
|
||||
p->desc[0] = 0;
|
||||
p->reset_disposition = RESET_DEDICATED;
|
||||
p->retry_pulse = PIN_AVR_SCK;
|
||||
p->flags = AVRPART_SERIALOK | AVRPART_PARALLELOK | AVRPART_ENABLEPAGEPROGRAMMING;
|
||||
p->lineno = 0;
|
||||
memset(p->signature, 0xFF, 3);
|
||||
p->ctl_stack_type = CTL_STACK_NONE;
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
AVRPART * avr_dup_part(AVRPART * d)
|
||||
{
|
||||
AVRPART * p;
|
||||
|
||||
p = avr_new_part();
|
||||
*p = *d;
|
||||
|
||||
/* NEW */
|
||||
p->sigmem = avr_dup_mem(p->sigmem);
|
||||
p->flashmem = avr_dup_mem(p->flashmem);
|
||||
p->lfusemem = avr_dup_mem(p->lfusemem);
|
||||
p->hfusemem = avr_dup_mem(p->hfusemem);
|
||||
|
||||
return p;
|
||||
}
|
||||
@@ -0,0 +1,224 @@
|
||||
/*
|
||||
* avrdude - A Downloader/Uploader for AVR device programmers
|
||||
* Copyright (C) 2003-2004 Brian S. Dean <bsd@bsdhome.com>
|
||||
* Copyright (C) 2006 Joerg Wunsch <j@uriah.heep.sax.de>
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
/* $Id: avrpart.h 916 2010-01-15 16:36:13Z joerg_wunsch $ */
|
||||
|
||||
#ifndef avrpart_h
|
||||
#define avrpart_h
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
/*
|
||||
* AVR serial programming instructions
|
||||
*/
|
||||
enum {
|
||||
AVR_OP_READ,
|
||||
AVR_OP_WRITE,
|
||||
AVR_OP_READ_LO,
|
||||
AVR_OP_READ_HI,
|
||||
AVR_OP_WRITE_LO,
|
||||
AVR_OP_WRITE_HI,
|
||||
AVR_OP_LOADPAGE_LO,
|
||||
AVR_OP_LOADPAGE_HI,
|
||||
AVR_OP_LOAD_EXT_ADDR,
|
||||
AVR_OP_WRITEPAGE,
|
||||
AVR_OP_CHIP_ERASE,
|
||||
AVR_OP_PGM_ENABLE,
|
||||
AVR_OP_MAX
|
||||
};
|
||||
|
||||
|
||||
enum {
|
||||
AVR_CMDBIT_IGNORE, /* bit is ignored on input and output */
|
||||
AVR_CMDBIT_VALUE, /* bit is set to 0 or 1 for input or output */
|
||||
AVR_CMDBIT_ADDRESS, /* this bit represents an input address bit */
|
||||
AVR_CMDBIT_INPUT, /* this bit is an input bit */
|
||||
AVR_CMDBIT_OUTPUT /* this bit is an output bit */
|
||||
};
|
||||
|
||||
enum { /* these are assigned to reset_disposition of AVRPART */
|
||||
RESET_DEDICATED, /* reset pin is dedicated */
|
||||
RESET_IO /* reset pin might be configured as an I/O pin */
|
||||
};
|
||||
|
||||
enum ctl_stack_t {
|
||||
CTL_STACK_NONE, /* no control stack defined */
|
||||
CTL_STACK_PP, /* parallel programming control stack */
|
||||
CTL_STACK_HVSP /* high voltage serial programming control stack */
|
||||
};
|
||||
|
||||
/*
|
||||
* serial programming instruction bit specifications
|
||||
*/
|
||||
typedef struct cmdbit {
|
||||
int type; /* AVR_CMDBIT_* */
|
||||
int bitno; /* which input bit to use for this command bit */
|
||||
int value; /* bit value if type == AVR_CMDBIT_VALUD */
|
||||
} CMDBIT;
|
||||
|
||||
typedef struct opcode {
|
||||
CMDBIT bit[32]; /* opcode bit specs */
|
||||
} OPCODE;
|
||||
|
||||
|
||||
#define AVRPART_SERIALOK 0x0001 /* part supports serial programming */
|
||||
#define AVRPART_PARALLELOK 0x0002 /* part supports parallel programming */
|
||||
#define AVRPART_PSEUDOPARALLEL 0x0004 /* part has pseudo parallel support */
|
||||
#define AVRPART_HAS_JTAG 0x0008 /* part has a JTAG i/f */
|
||||
#define AVRPART_ALLOWFULLPAGEBITSTREAM 0x0010 /* JTAG ICE mkII param. */
|
||||
#define AVRPART_ENABLEPAGEPROGRAMMING 0x0020 /* JTAG ICE mkII param. */
|
||||
#define AVRPART_HAS_DW 0x0040 /* part has a debugWire i/f */
|
||||
#define AVRPART_HAS_PDI 0x0080 /* part has PDI i/f rather than ISP (ATxmega) */
|
||||
#define AVRPART_AVR32 0x0100 /* part is in AVR32 family */
|
||||
#define AVRPART_INIT_SMC 0x0200 /* part will undergo chip erase */
|
||||
#define AVRPART_WRITE 0x0400 /* at least one write operation specified */
|
||||
#define AVRPART_HAS_TPI 0x0800 /* part has TPI i/f rather than ISP (ATtiny4/5/9/10) */
|
||||
|
||||
#define AVR_DESCLEN 64
|
||||
#define AVR_IDLEN 32
|
||||
#define CTL_STACK_SIZE 32
|
||||
#define FLASH_INSTR_SIZE 3
|
||||
#define EEPROM_INSTR_SIZE 20
|
||||
|
||||
#define TAG_ALLOCATED 1 /* memory byte is allocated */
|
||||
|
||||
#define AVR_MEMDESCLEN 64
|
||||
typedef struct avrmem {
|
||||
char desc[AVR_MEMDESCLEN]; /* memory description ("flash", "eeprom", etc) */
|
||||
int paged; /* page addressed (e.g. ATmega flash) */
|
||||
int size; /* total memory size in bytes */
|
||||
int page_size; /* size of memory page (if page addressed) */
|
||||
int num_pages; /* number of pages (if page addressed) */
|
||||
unsigned int offset; /* offset in IO memory (ATxmega) */
|
||||
int min_write_delay; /* microseconds */
|
||||
int max_write_delay; /* microseconds */
|
||||
int pwroff_after_write; /* after this memory type is written to,
|
||||
the device must be powered off and
|
||||
back on, see errata
|
||||
http://www.atmel.com/atmel/acrobat/doc1280.pdf */
|
||||
unsigned char readback[2]; /* polled read-back values */
|
||||
|
||||
int mode; /* stk500 v2 xml file parameter */
|
||||
int delay; /* stk500 v2 xml file parameter */
|
||||
int blocksize; /* stk500 v2 xml file parameter */
|
||||
int readsize; /* stk500 v2 xml file parameter */
|
||||
int pollindex; /* stk500 v2 xml file parameter */
|
||||
|
||||
unsigned char * buf; /* pointer to memory buffer */
|
||||
unsigned char tags[1024*1024]; /* allocation tags */
|
||||
OPCODE * op[AVR_OP_MAX]; /* opcodes */
|
||||
} AVRMEM;
|
||||
|
||||
typedef struct avrpart {
|
||||
char desc[AVR_DESCLEN]; /* long part name */
|
||||
char id[AVR_IDLEN]; /* short part name */
|
||||
int stk500_devcode; /* stk500 device code */
|
||||
int avr910_devcode; /* avr910 device code */
|
||||
int chip_erase_delay; /* microseconds */
|
||||
unsigned char pagel; /* for parallel programming */
|
||||
unsigned char bs2; /* for parallel programming */
|
||||
unsigned char signature[3]; /* expected value of signature bytes */
|
||||
int reset_disposition; /* see RESET_ enums */
|
||||
int retry_pulse; /* retry program enable by pulsing
|
||||
this pin (PIN_AVR_*) */
|
||||
unsigned flags; /* see AVRPART_ masks */
|
||||
|
||||
int timeout; /* stk500 v2 xml file parameter */
|
||||
int stabdelay; /* stk500 v2 xml file parameter */
|
||||
int cmdexedelay; /* stk500 v2 xml file parameter */
|
||||
int synchloops; /* stk500 v2 xml file parameter */
|
||||
int bytedelay; /* stk500 v2 xml file parameter */
|
||||
int pollindex; /* stk500 v2 xml file parameter */
|
||||
unsigned char pollvalue; /* stk500 v2 xml file parameter */
|
||||
int predelay; /* stk500 v2 xml file parameter */
|
||||
int postdelay; /* stk500 v2 xml file parameter */
|
||||
int pollmethod; /* stk500 v2 xml file parameter */
|
||||
|
||||
enum ctl_stack_t ctl_stack_type; /* what to use the ctl stack for */
|
||||
unsigned char controlstack[CTL_STACK_SIZE]; /* stk500v2 PP/HVSP ctl stack */
|
||||
unsigned char flash_instr[FLASH_INSTR_SIZE]; /* flash instructions (debugWire, JTAG) */
|
||||
unsigned char eeprom_instr[EEPROM_INSTR_SIZE]; /* EEPROM instructions (debugWire, JTAG) */
|
||||
|
||||
int hventerstabdelay; /* stk500 v2 hv mode parameter */
|
||||
int progmodedelay; /* stk500 v2 hv mode parameter */
|
||||
int latchcycles; /* stk500 v2 hv mode parameter */
|
||||
int togglevtg; /* stk500 v2 hv mode parameter */
|
||||
int poweroffdelay; /* stk500 v2 hv mode parameter */
|
||||
int resetdelayms; /* stk500 v2 hv mode parameter */
|
||||
int resetdelayus; /* stk500 v2 hv mode parameter */
|
||||
int hvleavestabdelay; /* stk500 v2 hv mode parameter */
|
||||
int resetdelay; /* stk500 v2 hv mode parameter */
|
||||
int chiperasepulsewidth; /* stk500 v2 hv mode parameter */
|
||||
int chiperasepolltimeout; /* stk500 v2 hv mode parameter */
|
||||
int chiperasetime; /* stk500 v2 hv mode parameter */
|
||||
int programfusepulsewidth; /* stk500 v2 hv mode parameter */
|
||||
int programfusepolltimeout; /* stk500 v2 hv mode parameter */
|
||||
int programlockpulsewidth; /* stk500 v2 hv mode parameter */
|
||||
int programlockpolltimeout; /* stk500 v2 hv mode parameter */
|
||||
int synchcycles; /* stk500 v2 hv mode parameter */
|
||||
int hvspcmdexedelay; /* stk500 v2 xml file parameter */
|
||||
|
||||
unsigned char idr; /* JTAG ICE mkII XML file parameter */
|
||||
unsigned char rampz; /* JTAG ICE mkII XML file parameter */
|
||||
unsigned char spmcr; /* JTAG ICE mkII XML file parameter */
|
||||
unsigned short eecr; /* JTAC ICE mkII XML file parameter */
|
||||
unsigned int nvm_base; /* Base address of NVM controller in ATxmega devices */
|
||||
|
||||
OPCODE * op[AVR_OP_MAX]; /* opcodes */
|
||||
|
||||
AVRMEM *sigmem;
|
||||
AVRMEM *flashmem;
|
||||
AVRMEM *lfusemem;
|
||||
AVRMEM *hfusemem;
|
||||
AVRMEM *fusesmem;
|
||||
AVRMEM *lockmem;
|
||||
char config_file[4096]; /* config file where defined */
|
||||
int lineno; /* config file line number */
|
||||
} AVRPART;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Functions for OPCODE structures */
|
||||
OPCODE * avr_new_opcode(void);
|
||||
int avr_set_bits(OPCODE * op, unsigned char * cmd);
|
||||
int avr_set_addr(OPCODE * op, unsigned char * cmd, unsigned long addr);
|
||||
int avr_set_input(OPCODE * op, unsigned char * cmd, unsigned char data);
|
||||
int avr_get_output(OPCODE * op, unsigned char * res, unsigned char * data);
|
||||
|
||||
/* Functions for AVRMEM structures */
|
||||
AVRMEM * avr_new_memtype(void);
|
||||
int avr_initmem(AVRPART * p);
|
||||
AVRMEM * avr_dup_mem(AVRMEM * m);
|
||||
AVRMEM * avr_locate_mem(AVRPART * p, char * desc);
|
||||
|
||||
/* Functions for AVRPART structures */
|
||||
AVRPART * avr_new_part(void);
|
||||
AVRPART * avr_dup_part(AVRPART * d);
|
||||
typedef void (*walk_avrparts_cb)(const char *name, const char *desc,
|
||||
const char *cfgname, int cfglineno,
|
||||
void *cookie);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* avrpart_h */
|
||||
@@ -0,0 +1,357 @@
|
||||
/*
|
||||
* avrdude - A Downloader/Uploader for AVR device programmers
|
||||
* Copyright (C) 2000-2005 Brian S. Dean <bsd@bsdhome.com>
|
||||
* Copyright (C) 2007 Joerg Wunsch
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
/* $Id: AVRUPD.c 819 2009-04-28 18:35:14Z joerg_wunsch $ */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "../pilight/core/log.h"
|
||||
#include "../pilight/core/mem.h"
|
||||
#include "avrdude.h"
|
||||
#include "avr.h"
|
||||
#include "avrconfig.h"
|
||||
#include "fileio.h"
|
||||
#include "avrupd.h"
|
||||
|
||||
AVRUPD * parse_op(char * s)
|
||||
{
|
||||
char buf[1024];
|
||||
char * p, * cp, c;
|
||||
AVRUPD * upd;
|
||||
int i;
|
||||
size_t fnlen;
|
||||
|
||||
upd = (AVRUPD *)MALLOC(sizeof(AVRUPD));
|
||||
if (upd == NULL) {
|
||||
fprintf(stderr, "out of memory\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
i = 0;
|
||||
p = s;
|
||||
while ((i < (sizeof(buf)-1) && *p && (*p != ':')))
|
||||
buf[i++] = *p++;
|
||||
buf[i] = 0;
|
||||
|
||||
if (*p != ':') {
|
||||
upd->memtype = (char *)MALLOC(strlen("flash")+1);
|
||||
if (upd->memtype == NULL) {
|
||||
outofmem:
|
||||
fprintf(stderr, "out of memory\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
strcpy(upd->memtype, "flash");
|
||||
upd->op = DEVICE_WRITE;
|
||||
upd->filename = (char *)MALLOC(strlen(buf) + 1);
|
||||
if (upd->filename == NULL)
|
||||
goto outofmem;
|
||||
strcpy(upd->filename, buf);
|
||||
upd->format = FMT_AUTO;
|
||||
return upd;
|
||||
}
|
||||
|
||||
upd->memtype = (char *)MALLOC(strlen(buf)+1);
|
||||
if (upd->memtype == NULL) {
|
||||
fprintf(stderr, "out of memory\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
strcpy(upd->memtype, buf);
|
||||
|
||||
p++;
|
||||
if (*p == 'r') {
|
||||
upd->op = DEVICE_READ;
|
||||
}
|
||||
else if (*p == 'w') {
|
||||
upd->op = DEVICE_WRITE;
|
||||
}
|
||||
else if (*p == 'v') {
|
||||
upd->op = DEVICE_VERIFY;
|
||||
}
|
||||
else {
|
||||
logprintf(LOG_ERR, "invalid I/O mode '%c' in update specification", *p);
|
||||
logprintf(LOG_ERR,
|
||||
" allowed values are:\n"
|
||||
" r = read device\n"
|
||||
" w = write device\n"
|
||||
" v = verify device");
|
||||
FREE(upd->memtype);
|
||||
FREE(upd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
p++;
|
||||
|
||||
if (*p != ':') {
|
||||
logprintf(LOG_ERR, "invalid update specification");
|
||||
FREE(upd->memtype);
|
||||
FREE(upd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
p++;
|
||||
|
||||
/*
|
||||
* Now, parse the filename component. Instead of looking for the
|
||||
* leftmost possible colon delimiter, we look for the rightmost one.
|
||||
* If we found one, we do have a trailing :format specifier, and
|
||||
* process it. Otherwise, the remainder of the string is our file
|
||||
* name component. That way, the file name itself is allowed to
|
||||
* contain a colon itself (e. g. C:/some/file.hex), except the
|
||||
* optional format specifier becomes mandatory then.
|
||||
*/
|
||||
cp = p;
|
||||
p = strrchr(cp, ':');
|
||||
if (p == NULL) {
|
||||
upd->format = FMT_AUTO;
|
||||
fnlen = strlen(cp);
|
||||
upd->filename = (char *)MALLOC(fnlen + 1);
|
||||
} else {
|
||||
fnlen = p - cp;
|
||||
upd->filename = (char *)MALLOC(fnlen +1);
|
||||
c = *++p;
|
||||
if (c && p[1])
|
||||
/* More than one char - force failure below. */
|
||||
c = '?';
|
||||
switch (c) {
|
||||
case 'a': upd->format = FMT_AUTO; break;
|
||||
case 's': upd->format = FMT_SREC; break;
|
||||
case 'i': upd->format = FMT_IHEX; break;
|
||||
case 'r': upd->format = FMT_RBIN; break;
|
||||
case 'm': upd->format = FMT_IMM; break;
|
||||
case 'b': upd->format = FMT_BIN; break;
|
||||
case 'd': upd->format = FMT_DEC; break;
|
||||
case 'h': upd->format = FMT_HEX; break;
|
||||
case 'o': upd->format = FMT_OCT; break;
|
||||
default:
|
||||
logprintf(LOG_ERR, "invalid file format '%s' in update specifier", p);
|
||||
FREE(upd->memtype);
|
||||
FREE(upd);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (upd->filename == NULL) {
|
||||
fprintf(stderr, "out of memory\n");
|
||||
FREE(upd->memtype);
|
||||
FREE(upd);
|
||||
return NULL;
|
||||
}
|
||||
memcpy(upd->filename, cp, fnlen);
|
||||
upd->filename[fnlen] = 0;
|
||||
|
||||
return upd;
|
||||
}
|
||||
|
||||
AVRUPD * dup_AVRUPD(AVRUPD * upd)
|
||||
{
|
||||
AVRUPD * u;
|
||||
|
||||
u = (AVRUPD *)MALLOC(sizeof(AVRUPD));
|
||||
if(u == NULL) {
|
||||
fprintf(stderr, "out of memory\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
memcpy(u, upd, sizeof(AVRUPD));
|
||||
|
||||
if((u->memtype = MALLOC(strlen(upd->memtype)+1)) == NULL) {
|
||||
fprintf(stderr, "out of memory\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
strcpy(u->memtype, upd->memtype);
|
||||
if((u->filename = MALLOC(strlen(upd->filename)+1)) == NULL) {
|
||||
fprintf(stderr, "out of memory\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
strcpy(u->filename, upd->filename);
|
||||
|
||||
return u;
|
||||
}
|
||||
|
||||
AVRUPD * new_AVRUPD(int op, char * memtype, int filefmt, char * filename)
|
||||
{
|
||||
AVRUPD * u;
|
||||
|
||||
u = (AVRUPD *)MALLOC(sizeof(AVRUPD));
|
||||
if (u == NULL) {
|
||||
fprintf(stderr, "out of memory\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
u->memtype = MALLOC(strlen(memtype)+1);
|
||||
strcpy(u->memtype, memtype);
|
||||
u->filename = MALLOC(strlen(filename)+1);
|
||||
strcpy(u->filename, filename);
|
||||
u->op = op;
|
||||
u->format = filefmt;
|
||||
|
||||
return u;
|
||||
}
|
||||
|
||||
int do_op(PROGRAMMER * pgm, struct avrpart * p, AVRUPD * upd, int nowrite,
|
||||
int verify)
|
||||
{
|
||||
struct avrpart * v;
|
||||
AVRMEM * mem = NULL;
|
||||
int size, vsize;
|
||||
int rc;
|
||||
|
||||
/* NEW */
|
||||
if(strcmp(upd->memtype, "flash") == 0) {
|
||||
mem = p->flashmem;
|
||||
} else if(strcmp(upd->memtype, "lfuse") == 0) {
|
||||
mem = p->lfusemem;
|
||||
} else if(strcmp(upd->memtype, "hfuse") == 0) {
|
||||
mem = p->hfusemem;
|
||||
}
|
||||
|
||||
if (mem == NULL) {
|
||||
logprintf(LOG_ERR, "\"%s\" memory type not defined for part \"%s\"", upd->memtype, p->desc);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (upd->op == DEVICE_READ) {
|
||||
/*
|
||||
* read out the specified device memory and write it to a file
|
||||
*/
|
||||
|
||||
report_progress(0,1,"Reading");
|
||||
rc = avr_read(pgm, p, upd->memtype, 0);
|
||||
if (rc < 0) {
|
||||
logprintf(LOG_ERR, "failed to read all of %s memory, rc=%d", mem->desc, rc);
|
||||
return -1;
|
||||
}
|
||||
report_progress(1,1,NULL);
|
||||
size = rc;
|
||||
|
||||
logprintf(LOG_INFO,
|
||||
"writing output file \"%s\"",
|
||||
strcmp(upd->filename, "-")==0 ? "<stdout>" : upd->filename);
|
||||
|
||||
rc = fileio(FIO_WRITE, upd->filename, upd->format, p, upd->memtype, size);
|
||||
if (rc < 0) {
|
||||
logprintf(LOG_ERR, "write to file '%s' failed", upd->filename);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else if (upd->op == DEVICE_WRITE) {
|
||||
/*
|
||||
* write the selected device memory using data from a file; first
|
||||
* read the data from the specified file
|
||||
*/
|
||||
|
||||
logprintf(LOG_INFO,
|
||||
"reading input file \"%s\"",
|
||||
strcmp(upd->filename, "-")==0 ? "<stdin>" : upd->filename);
|
||||
|
||||
rc = fileio(FIO_READ, upd->filename, upd->format, p, upd->memtype, -1);
|
||||
if (rc < 0) {
|
||||
logprintf(LOG_INFO, "write to file '%s' failed", upd->filename);
|
||||
return -1;
|
||||
}
|
||||
size = rc;
|
||||
|
||||
/*
|
||||
* write the buffer contents to the selected memory type
|
||||
*/
|
||||
logprintf(LOG_INFO, "writing %s (%d bytes)", mem->desc, size);
|
||||
|
||||
if (!nowrite) {
|
||||
report_progress(0,1,"Writing");
|
||||
rc = avr_write(pgm, p, upd->memtype, size);
|
||||
report_progress(1,1,NULL);
|
||||
}
|
||||
else {
|
||||
/*
|
||||
* test mode, don't actually write to the chip, output the buffer
|
||||
* to stdout in intel hex instead
|
||||
*/
|
||||
rc = fileio(FIO_WRITE, "-", FMT_IHEX, p, upd->memtype, size);
|
||||
}
|
||||
|
||||
if (rc < 0) {
|
||||
logprintf(LOG_ERR, "failed to write %s memory, rc=%d", mem->desc, rc);
|
||||
return -1;
|
||||
}
|
||||
|
||||
vsize = rc;
|
||||
|
||||
logprintf(LOG_INFO, "%d bytes of %s written", vsize, mem->desc);
|
||||
|
||||
}
|
||||
else if (upd->op == DEVICE_VERIFY) {
|
||||
/*
|
||||
* verify that the in memory file (p->mem[AVR_M_FLASH|AVR_M_EEPROM])
|
||||
* is the same as what is on the chip
|
||||
*/
|
||||
v = avr_dup_part(p);
|
||||
|
||||
logprintf(LOG_INFO, "verifying %s memory against %s:", mem->desc, upd->filename);
|
||||
logprintf(LOG_INFO, "load data %s data from input file %s:", mem->desc, upd->filename);
|
||||
|
||||
rc = fileio(FIO_READ, upd->filename, upd->format, p, upd->memtype, -1);
|
||||
if (rc < 0) {
|
||||
logprintf(LOG_ERR, "read from file '%s' failed", upd->filename);
|
||||
return -1;
|
||||
}
|
||||
size = rc;
|
||||
|
||||
logprintf(LOG_INFO, "input file %s contains %d bytes", upd->filename, size);
|
||||
|
||||
report_progress (0,1,"Reading");
|
||||
rc = avr_read(pgm, v, upd->memtype, size);
|
||||
if (rc < 0) {
|
||||
logprintf(LOG_ERR, "failed to read all of %s memory, rc=%d", mem->desc, rc);
|
||||
return -1;
|
||||
}
|
||||
report_progress (1,1,NULL);
|
||||
|
||||
logprintf(LOG_INFO, "verifying ...");
|
||||
|
||||
rc = avr_verify(p, v, upd->memtype, size);
|
||||
if (rc < 0) {
|
||||
logprintf(LOG_ERR, "verification error; content mismatch");
|
||||
return -1;
|
||||
}
|
||||
|
||||
logprintf(LOG_INFO, "%d bytes of %s verified", rc, mem->desc);
|
||||
|
||||
FREE(v->sigmem->buf);
|
||||
FREE(v->sigmem);
|
||||
FREE(v->flashmem->buf);
|
||||
FREE(v->flashmem);
|
||||
FREE(v->hfusemem->buf);
|
||||
FREE(v->hfusemem);
|
||||
FREE(v->lfusemem->buf);
|
||||
FREE(v->lfusemem);
|
||||
FREE(v);
|
||||
}
|
||||
else {
|
||||
logprintf(LOG_ERR, "invalid update operation (%d) requested", upd->op);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* avrdude - A Downloader/Uploader for AVR device programmers
|
||||
* Copyright (C) 2000-2005 Brian S. Dean <bsd@bsdhome.com>
|
||||
* Copyright (C) 2007 Joerg Wunsch
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
/* $Id: AVRUPD.h 722 2007-01-24 22:43:46Z joerg_wunsch $ */
|
||||
|
||||
#ifndef avrupd_h
|
||||
#define avrupd_h
|
||||
|
||||
enum {
|
||||
DEVICE_READ,
|
||||
DEVICE_WRITE,
|
||||
DEVICE_VERIFY
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
char * memtype;
|
||||
int op;
|
||||
char * filename;
|
||||
int format;
|
||||
} AVRUPD;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
AVRUPD * parse_op(char * s);
|
||||
AVRUPD * dup_AVRUPD(AVRUPD * upd);
|
||||
AVRUPD * new_AVRUPD(int op, char * memtype, int filefmt, char * filename);
|
||||
int do_op(PROGRAMMER * pgm, struct avrpart * p, AVRUPD * upd, int nowrite, int verify);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,544 @@
|
||||
/*
|
||||
* avrdude - A Downloader/Uploader for AVR device programmers
|
||||
* Copyright (C) 2000-2004 Brian S. Dean <bsd@bsdhome.com>
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
/* $Id: fileio.c 929 2010-01-18 20:40:15Z joerg_wunsch $ */
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "../pilight/core/log.h"
|
||||
#include "../pilight/core/mem.h"
|
||||
#include "../pilight/core/common.h"
|
||||
#include "avrdude.h"
|
||||
#include "avr.h"
|
||||
#include "fileio.h"
|
||||
|
||||
#define IHEX_MAXDATA 256
|
||||
#define MAX_LINE_LEN 256 /* max line length for ASCII format input files */
|
||||
|
||||
struct ihexrec {
|
||||
unsigned char reclen;
|
||||
unsigned int loadofs;
|
||||
unsigned char rectyp;
|
||||
unsigned char data[IHEX_MAXDATA];
|
||||
unsigned char cksum;
|
||||
};
|
||||
|
||||
static int b2ihex(unsigned char * inbuf, int bufsize,
|
||||
int recsize, int startaddr,
|
||||
char * outfile, FILE * outf)
|
||||
{
|
||||
unsigned char * buf;
|
||||
unsigned int nextaddr;
|
||||
int n, nbytes, n_64k;
|
||||
int i;
|
||||
unsigned char cksum;
|
||||
|
||||
if (recsize > 255) {
|
||||
logprintf(LOG_ERR, "recsize=%d, must be < 256", recsize);
|
||||
return -1;
|
||||
}
|
||||
|
||||
n_64k = 0;
|
||||
nextaddr = startaddr;
|
||||
buf = inbuf;
|
||||
nbytes = 0;
|
||||
|
||||
while (bufsize) {
|
||||
n = recsize;
|
||||
if (n > bufsize)
|
||||
n = bufsize;
|
||||
|
||||
if ((nextaddr + n) > 0x10000)
|
||||
n = 0x10000 - nextaddr;
|
||||
|
||||
if (n) {
|
||||
cksum = 0;
|
||||
fprintf(outf, ":%02X%04X00", n, nextaddr);
|
||||
cksum += n + ((nextaddr >> 8) & 0x0ff) + (nextaddr & 0x0ff);
|
||||
for (i=0; i<n; i++) {
|
||||
fprintf(outf, "%02X", buf[i]);
|
||||
cksum += buf[i];
|
||||
}
|
||||
cksum = -cksum;
|
||||
fprintf(outf, "%02X\n", cksum);
|
||||
|
||||
nextaddr += n;
|
||||
nbytes += n;
|
||||
}
|
||||
|
||||
if (nextaddr >= 0x10000) {
|
||||
int lo, hi;
|
||||
/* output an extended address record */
|
||||
n_64k++;
|
||||
lo = n_64k & 0xff;
|
||||
hi = (n_64k >> 8) & 0xff;
|
||||
cksum = 0;
|
||||
fprintf(outf, ":02000004%02X%02X", hi, lo);
|
||||
cksum += 2 + 0 + 4 + hi + lo;
|
||||
cksum = -cksum;
|
||||
fprintf(outf, "%02X\n", cksum);
|
||||
nextaddr = 0;
|
||||
}
|
||||
|
||||
/* advance to next 'recsize' bytes */
|
||||
buf += n;
|
||||
bufsize -= n;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------
|
||||
add the end of record data line
|
||||
-----------------------------------------------------------------*/
|
||||
cksum = 0;
|
||||
n = 0;
|
||||
nextaddr = 0;
|
||||
fprintf(outf, ":%02X%04X01", n, nextaddr);
|
||||
cksum += n + ((nextaddr >> 8) & 0x0ff) + (nextaddr & 0x0ff) + 1;
|
||||
cksum = -cksum;
|
||||
fprintf(outf, "%02X\n", cksum);
|
||||
|
||||
return nbytes;
|
||||
}
|
||||
|
||||
|
||||
static int ihex_readrec(struct ihexrec * ihex, char * rec)
|
||||
{
|
||||
int i, j;
|
||||
char buf[8];
|
||||
int offset, len;
|
||||
char * e;
|
||||
unsigned char cksum;
|
||||
int rc;
|
||||
|
||||
len = strlen(rec);
|
||||
offset = 1;
|
||||
cksum = 0;
|
||||
|
||||
/* reclen */
|
||||
if (offset + 2 > len)
|
||||
return -1;
|
||||
for (i=0; i<2; i++)
|
||||
buf[i] = rec[offset++];
|
||||
buf[i] = 0;
|
||||
ihex->reclen = strtoul(buf, &e, 16);
|
||||
if (e == buf || *e != 0)
|
||||
return -1;
|
||||
|
||||
/* load offset */
|
||||
if (offset + 4 > len)
|
||||
return -1;
|
||||
for (i=0; i<4; i++)
|
||||
buf[i] = rec[offset++];
|
||||
buf[i] = 0;
|
||||
ihex->loadofs = strtoul(buf, &e, 16);
|
||||
if (e == buf || *e != 0)
|
||||
return -1;
|
||||
|
||||
/* record type */
|
||||
if (offset + 2 > len)
|
||||
return -1;
|
||||
for (i=0; i<2; i++)
|
||||
buf[i] = rec[offset++];
|
||||
buf[i] = 0;
|
||||
ihex->rectyp = strtoul(buf, &e, 16);
|
||||
if (e == buf || *e != 0)
|
||||
return -1;
|
||||
|
||||
cksum = ihex->reclen + ((ihex->loadofs >> 8) & 0x0ff) + (ihex->loadofs & 0x0ff) + ihex->rectyp;
|
||||
|
||||
/* data */
|
||||
for (j=0; j<ihex->reclen; j++) {
|
||||
if (offset + 2 > len)
|
||||
return -1;
|
||||
for (i=0; i<2; i++)
|
||||
buf[i] = rec[offset++];
|
||||
buf[i] = 0;
|
||||
ihex->data[j] = strtoul(buf, &e, 16);
|
||||
if (e == buf || *e != 0)
|
||||
return -1;
|
||||
cksum += ihex->data[j];
|
||||
}
|
||||
|
||||
/* cksum */
|
||||
if (offset + 2 > len)
|
||||
return -1;
|
||||
for (i=0; i<2; i++)
|
||||
buf[i] = rec[offset++];
|
||||
buf[i] = 0;
|
||||
ihex->cksum = strtoul(buf, &e, 16);
|
||||
if (e == buf || *e != 0)
|
||||
return -1;
|
||||
|
||||
rc = -cksum & 0x000000ff;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Intel Hex to binary buffer
|
||||
*
|
||||
* Given an open file 'inf' which contains Intel Hex formated data,
|
||||
* parse the file and lay it out within the memory buffer pointed to
|
||||
* by outbuf. The size of outbuf, 'bufsize' is honored; if data would
|
||||
* fall outsize of the memory buffer outbuf, an error is generated.
|
||||
*
|
||||
* Return the maximum memory address within 'outbuf' that was written.
|
||||
* If an error occurs, return -1.
|
||||
*
|
||||
* */
|
||||
static int ihex2b(char * infile, FILE * inf,
|
||||
unsigned char * outbuf, int bufsize)
|
||||
{
|
||||
char buffer [ MAX_LINE_LEN ];
|
||||
unsigned char * buf;
|
||||
unsigned int nextaddr, baseaddr, maxaddr, offsetaddr;
|
||||
int i;
|
||||
int lineno;
|
||||
int len;
|
||||
struct ihexrec ihex;
|
||||
int rc;
|
||||
|
||||
lineno = 0;
|
||||
buf = outbuf;
|
||||
baseaddr = 0;
|
||||
maxaddr = 0;
|
||||
offsetaddr = 0;
|
||||
nextaddr = 0;
|
||||
|
||||
while (fgets((char *)buffer,MAX_LINE_LEN,inf)!=NULL) {
|
||||
lineno++;
|
||||
len = strlen(buffer);
|
||||
if (buffer[len-1] == '\n')
|
||||
buffer[--len] = 0;
|
||||
if (buffer[0] != ':')
|
||||
continue;
|
||||
rc = ihex_readrec(&ihex, buffer);
|
||||
if (rc < 0) {
|
||||
logprintf(LOG_ERR, "invalid record at line %d of \"%s\"", lineno, infile);
|
||||
return -1;
|
||||
}
|
||||
else if (rc != ihex.cksum) {
|
||||
logprintf(LOG_ERR, "ERROR: checksum mismatch at line %d of \"%s\"", lineno, infile);
|
||||
logprintf(LOG_ERR, "checksum=0x%02x, computed checksum=0x%02x", ihex.cksum, rc);
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (ihex.rectyp) {
|
||||
case 0: /* data record */
|
||||
nextaddr = ihex.loadofs + baseaddr;
|
||||
if ((nextaddr + ihex.reclen) > (bufsize+offsetaddr)) {
|
||||
logprintf(LOG_ERR, "ERROR: address 0x%04x out of range at line %d of %s", nextaddr+ihex.reclen, lineno, infile);
|
||||
return -1;
|
||||
}
|
||||
for (i=0; i<ihex.reclen; i++) {
|
||||
buf[nextaddr+i-offsetaddr] = ihex.data[i];
|
||||
}
|
||||
if (nextaddr+ihex.reclen > maxaddr)
|
||||
maxaddr = nextaddr+ihex.reclen;
|
||||
break;
|
||||
|
||||
case 1: /* end of file record */
|
||||
return maxaddr-offsetaddr;
|
||||
break;
|
||||
|
||||
case 2: /* extended segment address record */
|
||||
baseaddr = (ihex.data[0] << 8 | ihex.data[1]) << 4;
|
||||
break;
|
||||
|
||||
case 3: /* start segment address record */
|
||||
/* we don't do anything with the start address */
|
||||
break;
|
||||
|
||||
case 4: /* extended linear address record */
|
||||
baseaddr = (ihex.data[0] << 8 | ihex.data[1]) << 16;
|
||||
if(nextaddr == 0) offsetaddr = baseaddr; // if provided before any data, then remember it
|
||||
break;
|
||||
|
||||
case 5: /* start linear address record */
|
||||
/* we don't do anything with the start address */
|
||||
break;
|
||||
|
||||
default:
|
||||
logprintf(LOG_ERR, "don't know how to deal with rectype=%d at line %d of %s", ihex.rectyp, lineno, infile);
|
||||
return -1;
|
||||
break;
|
||||
}
|
||||
|
||||
} /* while */
|
||||
|
||||
logprintf(LOG_ERR, "no end of file record found for Intel Hex file \"%s\"", infile);
|
||||
|
||||
return maxaddr-offsetaddr;
|
||||
}
|
||||
|
||||
static int fileio_imm(struct fioparms * fio,
|
||||
char * filename, FILE * f, unsigned char * buf, int size)
|
||||
{
|
||||
int rc = 0;
|
||||
char *e = NULL, **array = NULL;
|
||||
unsigned long b = 0;
|
||||
int loc = 0;
|
||||
unsigned int n = 0, i = 0;
|
||||
|
||||
switch (fio->op) {
|
||||
case FIO_READ:
|
||||
loc = 0;
|
||||
n = explode(filename, " ,", &array);
|
||||
for(i=0;i<n;i++) {
|
||||
if(loc < size) {
|
||||
b = strtoul(array[i], &e, 0);
|
||||
/* check for binary formated (0b10101001) strings */
|
||||
b = (strncmp (array[i], "0b", 2))?
|
||||
strtoul (array[i], &e, 0):
|
||||
strtoul (array[i] + 2, &e, 2);
|
||||
if (*e != 0) {
|
||||
logprintf(LOG_ERR, "invalid byte value (%s) specified for immediate mode", array[i]);
|
||||
array_free(&array, n);
|
||||
return -1;
|
||||
}
|
||||
buf[loc++] = b;
|
||||
rc = loc;
|
||||
}
|
||||
}
|
||||
array_free(&array, n);
|
||||
break;
|
||||
default:
|
||||
logprintf(LOG_ERR, "fileio: invalid operation=%d", fio->op);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (rc < 0 || (fio->op == FIO_WRITE && rc < size)) {
|
||||
logprintf(LOG_ERR, "%s error %s %s: %s; %s %d of the expected %d bytes",
|
||||
fio->iodesc, fio->dir, filename, strerror(errno), fio->rw, rc, size);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static int fileio_ihex(struct fioparms * fio,
|
||||
char * filename, FILE * f, unsigned char * buf, int size)
|
||||
{
|
||||
int rc;
|
||||
|
||||
switch (fio->op) {
|
||||
case FIO_WRITE:
|
||||
rc = b2ihex(buf, size, 32, 0, filename, f);
|
||||
if (rc < 0) {
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
|
||||
case FIO_READ:
|
||||
rc = ihex2b(filename, f, buf, size);
|
||||
if (rc < 0)
|
||||
return -1;
|
||||
break;
|
||||
|
||||
default:
|
||||
logprintf(LOG_ERR, "invalid Intex Hex file I/O operation=%d", fio->op);
|
||||
return -1;
|
||||
break;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int fileio_setparms(int op, struct fioparms * fp)
|
||||
{
|
||||
fp->op = op;
|
||||
|
||||
switch (op) {
|
||||
case FIO_READ:
|
||||
fp->mode = "r";
|
||||
fp->iodesc = "input";
|
||||
fp->dir = "from";
|
||||
fp->rw = "read";
|
||||
break;
|
||||
|
||||
case FIO_WRITE:
|
||||
fp->mode = "w";
|
||||
fp->iodesc = "output";
|
||||
fp->dir = "to";
|
||||
fp->rw = "wrote";
|
||||
break;
|
||||
|
||||
default:
|
||||
logprintf(LOG_ERR, "invalid I/O operation %d", op);
|
||||
return -1;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fmt_autodetect(char * fname)
|
||||
{
|
||||
FILE * f;
|
||||
unsigned char buf[MAX_LINE_LEN];
|
||||
int i;
|
||||
int len;
|
||||
int found;
|
||||
|
||||
f = fopen(fname, "r");
|
||||
if (f == NULL) {
|
||||
logprintf(LOG_ERR, "error opening %s: %s", fname, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (fgets((char *)buf, MAX_LINE_LEN, f)!=NULL) {
|
||||
buf[MAX_LINE_LEN-1] = 0;
|
||||
len = strlen((char *)buf);
|
||||
if (buf[len-1] == '\n')
|
||||
buf[--len] = 0;
|
||||
|
||||
/* check for binary data */
|
||||
found = 0;
|
||||
for (i=0; i<len; i++) {
|
||||
if (buf[i] > 127) {
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found) {
|
||||
fclose(f);
|
||||
return FMT_RBIN;
|
||||
}
|
||||
|
||||
/* check for lines that look like intel hex */
|
||||
if ((buf[0] == ':') && (len >= 11)) {
|
||||
found = 1;
|
||||
for (i=1; i<len; i++) {
|
||||
if (!isxdigit(buf[1])) {
|
||||
found = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found) {
|
||||
fclose(f);
|
||||
return FMT_IHEX;
|
||||
}
|
||||
}
|
||||
|
||||
/* check for lines that look like motorola s-record */
|
||||
if ((buf[0] == 'S') && (len >= 10) && isdigit(buf[1])) {
|
||||
found = 1;
|
||||
for (i=1; i<len; i++) {
|
||||
if (!isxdigit(buf[1])) {
|
||||
found = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found) {
|
||||
fclose(f);
|
||||
return FMT_SREC;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int fileio(int op, char * filename, FILEFMT format,
|
||||
struct avrpart * p, char * memtype, int size)
|
||||
{
|
||||
int rc;
|
||||
FILE * f;
|
||||
char * fname;
|
||||
unsigned char * buf;
|
||||
struct fioparms fio;
|
||||
AVRMEM * mem = NULL;
|
||||
|
||||
if(strcmp(memtype, "flash") == 0) {
|
||||
mem = p->flashmem;
|
||||
} else if(strcmp(memtype, "lfuse") == 0) {
|
||||
mem = p->lfusemem;
|
||||
} else if(strcmp(memtype, "hfuse") == 0) {
|
||||
mem = p->hfusemem;
|
||||
}
|
||||
if (mem == NULL) {
|
||||
logprintf(LOG_ERR, "fileio(): memory type \"%s\" not configured for device \"%s\"", memtype, p->desc);
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = fileio_setparms(op, &fio);
|
||||
if (rc < 0)
|
||||
return -1;
|
||||
|
||||
/* point at the requested memory buffer */
|
||||
buf = mem->buf;
|
||||
if (fio.op == FIO_READ)
|
||||
size = mem->size;
|
||||
|
||||
if (fio.op == FIO_READ) {
|
||||
/* 0xff fill unspecified memory */
|
||||
memset(buf, 0xff, size);
|
||||
}
|
||||
|
||||
fname = filename;
|
||||
f = NULL;
|
||||
|
||||
if (format == FMT_AUTO) {
|
||||
format = fmt_autodetect(fname);
|
||||
logprintf(LOG_INFO, "%s file %s auto detected as Intel Hex", fio.iodesc, fname);
|
||||
}
|
||||
|
||||
if (format != FMT_IMM) {
|
||||
f = fopen(fname, fio.mode);
|
||||
if (f == NULL) {
|
||||
logprintf(LOG_ERR, "can't open %s file %s: %s", fio.iodesc, fname, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
switch (format) {
|
||||
case FMT_IHEX:
|
||||
rc = fileio_ihex(&fio, fname, f, buf, size);
|
||||
break;
|
||||
case FMT_IMM:
|
||||
rc = fileio_imm(&fio, fname, f, buf, size);
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (rc > 0) {
|
||||
if ((op == FIO_READ) && (strcasecmp(mem->desc, "flash") == 0)) {
|
||||
/*
|
||||
* if we are reading flash, just mark the size as being the
|
||||
* highest non-0xff byte
|
||||
*/
|
||||
rc = avr_mem_hiaddr(mem);
|
||||
}
|
||||
}
|
||||
if (format != FMT_IMM) {
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* avrdude - A Downloader/Uploader for AVR device programmers
|
||||
* Copyright (C) 2000-2004 Brian S. Dean <bsd@bsdhome.com>
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
/* $Id: fileio.h 722 2007-01-24 22:43:46Z joerg_wunsch $ */
|
||||
|
||||
#ifndef fileio_h
|
||||
#define fileio_h
|
||||
|
||||
typedef enum {
|
||||
FMT_AUTO,
|
||||
FMT_SREC,
|
||||
FMT_IHEX,
|
||||
FMT_RBIN,
|
||||
FMT_IMM,
|
||||
FMT_HEX,
|
||||
FMT_DEC,
|
||||
FMT_OCT,
|
||||
FMT_BIN
|
||||
} FILEFMT;
|
||||
|
||||
struct fioparms {
|
||||
int op;
|
||||
char * mode;
|
||||
char * iodesc;
|
||||
char * dir;
|
||||
char * rw;
|
||||
};
|
||||
|
||||
enum {
|
||||
FIO_READ,
|
||||
FIO_WRITE
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
char * fmtstr(FILEFMT format);
|
||||
|
||||
int fileio_setparms(int op, struct fioparms * fp);
|
||||
|
||||
int fileio(int op, char * filename, FILEFMT format,
|
||||
struct avrpart * p, char * memtype, int size);
|
||||
|
||||
int fmt_autodetect(char * fname);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,174 @@
|
||||
/*
|
||||
* avrdude - A Downloader/Uploader for AVR device programmers
|
||||
* Copyright (C) 2002-2004 Brian S. Dean <bsd@bsdhome.com>
|
||||
* Copyright 2007 Joerg Wunsch <j@uriah.heep.sax.de>
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
/* $Id: pgm.c 797 2009-02-17 15:31:27Z joerg_wunsch $ */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "../pilight/core/log.h"
|
||||
#include "../pilight/core/mem.h"
|
||||
#include "avrdude.h"
|
||||
#include "pgm.h"
|
||||
|
||||
static int pgm_default_2 (struct programmer_t *, AVRPART *);
|
||||
static int pgm_default_3 (struct programmer_t * pgm, AVRPART * p, AVRMEM * mem,
|
||||
unsigned long addr, unsigned char * value);
|
||||
static void pgm_default_4 (struct programmer_t *);
|
||||
static int pgm_default_5 (struct programmer_t * pgm, AVRPART * p, AVRMEM * mem,
|
||||
unsigned long addr, unsigned char data);
|
||||
static void pgm_default_6 (struct programmer_t *, const char *);
|
||||
|
||||
|
||||
static int pgm_default_open (struct programmer_t *pgm, char * name) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pgm_default_led (struct programmer_t * pgm, int value)
|
||||
{
|
||||
/*
|
||||
* If programmer has no LEDs, just do nothing.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void pgm_default_powerup_powerdown (struct programmer_t * pgm)
|
||||
{
|
||||
/*
|
||||
* If programmer does not support powerup/down, just do nothing.
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
PROGRAMMER * pgm_new(void)
|
||||
{
|
||||
int i;
|
||||
PROGRAMMER * pgm;
|
||||
|
||||
pgm = (PROGRAMMER *)MALLOC(sizeof(*pgm));
|
||||
if (pgm == NULL) {
|
||||
fprintf(stderr, "out of memory\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
memset(pgm, 0, sizeof(*pgm));
|
||||
|
||||
pgm->desc[0] = 0;
|
||||
pgm->type[0] = 0;
|
||||
pgm->lineno = 0;
|
||||
pgm->baudrate = 0;
|
||||
|
||||
for (i=0; i<N_PINS; i++)
|
||||
pgm->pinno[i] = 0;
|
||||
|
||||
/*
|
||||
* mandatory functions - these are called without checking to see
|
||||
* whether they are assigned or not
|
||||
*/
|
||||
pgm->initialize = pgm_default_2;
|
||||
pgm->display = pgm_default_6;
|
||||
pgm->enable = pgm_default_4;
|
||||
pgm->disable = pgm_default_4;
|
||||
pgm->powerup = pgm_default_powerup_powerdown;
|
||||
pgm->powerdown = pgm_default_powerup_powerdown;
|
||||
pgm->program_enable = pgm_default_2;
|
||||
pgm->chip_erase = pgm_default_2;
|
||||
pgm->open = pgm_default_open;
|
||||
pgm->close = pgm_default_4;
|
||||
pgm->read_byte = pgm_default_3;
|
||||
pgm->write_byte = pgm_default_5;
|
||||
|
||||
/*
|
||||
* predefined functions - these functions have a valid default
|
||||
* implementation. Hence, they don't need to be defined in
|
||||
* the programmer.
|
||||
*/
|
||||
pgm->rdy_led = pgm_default_led;
|
||||
pgm->err_led = pgm_default_led;
|
||||
pgm->pgm_led = pgm_default_led;
|
||||
pgm->vfy_led = pgm_default_led;
|
||||
|
||||
/*
|
||||
* optional functions - these are checked to make sure they are
|
||||
* assigned before they are called
|
||||
*/
|
||||
pgm->cmd = NULL;
|
||||
pgm->spi = NULL;
|
||||
pgm->paged_write = NULL;
|
||||
pgm->paged_load = NULL;
|
||||
pgm->write_setup = NULL;
|
||||
pgm->read_sig_bytes = NULL;
|
||||
pgm->set_vtarget = NULL;
|
||||
pgm->set_varef = NULL;
|
||||
pgm->set_fosc = NULL;
|
||||
pgm->perform_osccal = NULL;
|
||||
pgm->setup = NULL;
|
||||
pgm->teardown = NULL;
|
||||
|
||||
return pgm;
|
||||
}
|
||||
|
||||
|
||||
static void pgm_default(void)
|
||||
{
|
||||
logprintf(LOG_NOTICE, "AVR programmer operation not supported");
|
||||
}
|
||||
|
||||
|
||||
static int pgm_default_2 (struct programmer_t * pgm, AVRPART * p)
|
||||
{
|
||||
pgm_default();
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int pgm_default_3 (struct programmer_t * pgm, AVRPART * p, AVRMEM * mem,
|
||||
unsigned long addr, unsigned char * value)
|
||||
{
|
||||
pgm_default();
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void pgm_default_4 (struct programmer_t * pgm)
|
||||
{
|
||||
pgm_default();
|
||||
}
|
||||
|
||||
static int pgm_default_5 (struct programmer_t * pgm, AVRPART * p, AVRMEM * mem,
|
||||
unsigned long addr, unsigned char data)
|
||||
{
|
||||
pgm_default();
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void pgm_default_6 (struct programmer_t * pgm, const char * p)
|
||||
{
|
||||
pgm_default();
|
||||
}
|
||||
|
||||
|
||||
// void programmer_display(PROGRAMMER * pgm, const char * p)
|
||||
// {
|
||||
// logprintf(LOG_INFO, "%sProgrammer Type : %s", p, pgm->type);
|
||||
// logprintf(LOG_INFO, "%sDescription : %s", p, pgm->desc);
|
||||
|
||||
// pgm->display(pgm, p);
|
||||
// }
|
||||
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* avrdude - A Downloader/Uploader for AVR device programmers
|
||||
* Copyright (C) 2002-2004 Brian S. Dean <bsd@bsdhome.com>
|
||||
* Copyright 2007 Joerg Wunsch <j@uriah.heep.sax.de>
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
/* $Id: pgm.h 797 2009-02-17 15:31:27Z joerg_wunsch $ */
|
||||
|
||||
#ifndef pgm_h
|
||||
#define pgm_h
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
#include "avrpart.h"
|
||||
#include "serial.h"
|
||||
#include "pindefs.h"
|
||||
|
||||
#define ON 1
|
||||
#define OFF 0
|
||||
|
||||
#define PGM_DESCLEN 80
|
||||
#define PGM_PORTLEN 4096
|
||||
#define PGM_TYPELEN 32
|
||||
|
||||
typedef enum {
|
||||
EXIT_VCC_UNSPEC,
|
||||
EXIT_VCC_ENABLED,
|
||||
EXIT_VCC_DISABLED
|
||||
} exit_vcc_t;
|
||||
|
||||
typedef enum {
|
||||
EXIT_RESET_UNSPEC,
|
||||
EXIT_RESET_ENABLED,
|
||||
EXIT_RESET_DISABLED
|
||||
} exit_reset_t;
|
||||
|
||||
typedef struct programmer_t {
|
||||
char desc[PGM_DESCLEN];
|
||||
char type[PGM_TYPELEN];
|
||||
char port[PGM_PORTLEN];
|
||||
unsigned int pinno[N_PINS];
|
||||
exit_vcc_t exit_vcc;
|
||||
exit_reset_t exit_reset;
|
||||
int ppidata;
|
||||
int ppictrl;
|
||||
int baudrate;
|
||||
double bitclock; /* JTAG ICE clock period in microseconds */
|
||||
int ispdelay; /* ISP clock delay */
|
||||
union filedescriptor fd;
|
||||
int page_size; /* page size if the programmer supports paged write/load */
|
||||
int (*rdy_led) (struct programmer_t * pgm, int value);
|
||||
int (*err_led) (struct programmer_t * pgm, int value);
|
||||
int (*pgm_led) (struct programmer_t * pgm, int value);
|
||||
int (*vfy_led) (struct programmer_t * pgm, int value);
|
||||
int (*initialize) (struct programmer_t * pgm, AVRPART * p);
|
||||
void (*display) (struct programmer_t * pgm, const char * p);
|
||||
void (*enable) (struct programmer_t * pgm);
|
||||
void (*disable) (struct programmer_t * pgm);
|
||||
void (*powerup) (struct programmer_t * pgm);
|
||||
void (*powerdown) (struct programmer_t * pgm);
|
||||
int (*program_enable) (struct programmer_t * pgm, AVRPART * p);
|
||||
int (*chip_erase) (struct programmer_t * pgm, AVRPART * p);
|
||||
int (*cmd) (struct programmer_t * pgm, unsigned char cmd[4],
|
||||
unsigned char res[4]);
|
||||
int (*spi) (struct programmer_t * pgm, unsigned char cmd[],
|
||||
unsigned char res[], int count);
|
||||
int (*open) (struct programmer_t * pgm, char * port);
|
||||
void (*close) (struct programmer_t * pgm);
|
||||
int (*paged_write) (struct programmer_t * pgm, AVRPART * p, AVRMEM * m,
|
||||
unsigned int page_size, unsigned int baseaddr,
|
||||
unsigned int n_bytes);
|
||||
int (*paged_load) (struct programmer_t * pgm, AVRPART * p, AVRMEM * m,
|
||||
unsigned int page_size, unsigned int baseaddr,
|
||||
unsigned int n_bytes);
|
||||
void (*write_setup) (struct programmer_t * pgm, AVRPART * p, AVRMEM * m);
|
||||
int (*write_byte) (struct programmer_t * pgm, AVRPART * p, AVRMEM * m,
|
||||
unsigned long addr, unsigned char value);
|
||||
int (*read_byte) (struct programmer_t * pgm, AVRPART * p, AVRMEM * m,
|
||||
unsigned long addr, unsigned char * value);
|
||||
int (*read_sig_bytes) (struct programmer_t * pgm, AVRPART * p, AVRMEM * m);
|
||||
void (*print_parms) (struct programmer_t * pgm);
|
||||
int (*set_vtarget) (struct programmer_t * pgm, double v);
|
||||
int (*set_varef) (struct programmer_t * pgm, unsigned int chan, double v);
|
||||
int (*set_fosc) (struct programmer_t * pgm, double v);
|
||||
int (*set_sck_period) (struct programmer_t * pgm, double v);
|
||||
int (*setpin) (struct programmer_t * pgm, int pin, int value);
|
||||
int (*getpin) (struct programmer_t * pgm, int pin);
|
||||
int (*highpulsepin) (struct programmer_t * pgm, int pin);
|
||||
int (*parseexitspecs) (struct programmer_t * pgm, char *s);
|
||||
int (*perform_osccal) (struct programmer_t * pgm);
|
||||
void (*setup) (struct programmer_t * pgm);
|
||||
void (*teardown) (struct programmer_t * pgm);
|
||||
char config_file[4096]; /* config file where defined */
|
||||
int lineno; /* config file line number */
|
||||
void *cookie; /* for private use by the programmer */
|
||||
char flag; /* for private use of the programmer */
|
||||
} PROGRAMMER;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
PROGRAMMER * pgm_new(void);
|
||||
|
||||
void programmer_display(PROGRAMMER * pgm, const char * p);
|
||||
|
||||
typedef void (*walk_programmers_cb)(const char *name, const char *desc,
|
||||
const char *cfgname, int cfglineno,
|
||||
void *cookie);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* avrdude - A Downloader/Uploader for AVR device programmers
|
||||
* Copyright (C) 2000-2004 Brian S. Dean <bsd@bsdhome.com>
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
/* $Id: pindefs.h 515 2005-09-18 20:12:23Z joerg_wunsch $ */
|
||||
|
||||
#ifndef __pindefs_h__
|
||||
#define __pindefs_h__
|
||||
|
||||
enum {
|
||||
PPI_AVR_VCC=1,
|
||||
PPI_AVR_BUFF,
|
||||
PIN_AVR_RESET,
|
||||
PIN_AVR_SCK,
|
||||
PIN_AVR_MOSI,
|
||||
PIN_AVR_MISO,
|
||||
PIN_LED_ERR,
|
||||
PIN_LED_RDY,
|
||||
PIN_LED_PGM,
|
||||
PIN_LED_VFY,
|
||||
N_PINS
|
||||
};
|
||||
#define PIN_INVERSE 0x80 /* flag for inverted pin in serbb */
|
||||
#define PIN_MASK 0x7f
|
||||
|
||||
#define LED_ON(fd,pin) ppi_setpin(fd,pin,0)
|
||||
#define LED_OFF(fd,pin) ppi_setpin(fd,pin,1)
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,297 @@
|
||||
/*
|
||||
* avrdude - A Downloader/Uploader for AVR device programmers
|
||||
* avrdude is Copyright (C) 2000-2004 Brian S. Dean <bsd@bsdhome.com>
|
||||
*
|
||||
* This file: Copyright (C) 2005-2007 Colin O'Flynn <coflynn@newae.com>
|
||||
*
|
||||
* 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 <stdio.h>
|
||||
|
||||
#include "../pilight/core/log.h"
|
||||
#include "avrdude.h"
|
||||
#include "avr.h"
|
||||
#include "pgm.h"
|
||||
#include "safemode.h"
|
||||
|
||||
/* This value from ac_cfg.h */
|
||||
/*
|
||||
* Writes the specified fuse in fusename (can be "lfuse", "hfuse", or
|
||||
* "efuse") and verifies it. Will try up to tries amount of times
|
||||
* before giving up
|
||||
*/
|
||||
int safemode_writefuse (unsigned char fuse, const char * fusename, PROGRAMMER * pgm,
|
||||
AVRPART * p, int tries)
|
||||
{
|
||||
AVRMEM * m = NULL;
|
||||
unsigned char fuseread;
|
||||
int returnvalue = -1;
|
||||
|
||||
// m = avr_locate_mem(p, fusename);
|
||||
if (m == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Keep trying to write then read back the fuse values */
|
||||
while (tries > 0) {
|
||||
if (avr_write_byte(pgm, p, m, 0, fuse) != 0) {
|
||||
continue;
|
||||
}
|
||||
if (pgm->read_byte(pgm, p, m, 0, &fuseread) != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Report information to user if needed */
|
||||
logprintf(LOG_ERR, "AVR wrote %s to %x, read as %x. %d attempts left", fusename, fuse, fuseread, tries-1);
|
||||
|
||||
/* If fuse wrote OK, no need to keep going */
|
||||
if (fuse == fuseread) {
|
||||
tries = 0;
|
||||
returnvalue = 0;
|
||||
}
|
||||
tries--;
|
||||
}
|
||||
|
||||
return returnvalue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reads the fuses three times, checking that all readings are the
|
||||
* same. This will ensure that the before values aren't in error!
|
||||
*/
|
||||
int safemode_readfuses (unsigned char * lfuse, unsigned char * hfuse,
|
||||
unsigned char * efuse, unsigned char * fuse,
|
||||
PROGRAMMER * pgm, AVRPART * p)
|
||||
{
|
||||
|
||||
unsigned char value;
|
||||
unsigned char fusegood = 0;
|
||||
unsigned char allowfuseread = 1;
|
||||
unsigned char safemode_lfuse;
|
||||
unsigned char safemode_hfuse;
|
||||
unsigned char safemode_efuse;
|
||||
unsigned char safemode_fuse;
|
||||
AVRMEM * m = NULL;
|
||||
|
||||
safemode_lfuse = *lfuse;
|
||||
safemode_hfuse = *hfuse;
|
||||
safemode_efuse = *efuse;
|
||||
safemode_fuse = *fuse;
|
||||
|
||||
|
||||
/* Read fuse three times */
|
||||
// fusegood = 2; /* If AVR device doesn't support this fuse, don't want
|
||||
// to generate a verify error */
|
||||
// m = avr_locate_mem(p, "fuse");
|
||||
// if (m != NULL) {
|
||||
// fusegood = 0; /* By default fuse is a failure */
|
||||
// if(pgm->read_byte(pgm, p, m, 0, &safemode_fuse) != 0)
|
||||
// {
|
||||
// allowfuseread = 0;
|
||||
// }
|
||||
// if(pgm->read_byte(pgm, p, m, 0, &value) != 0)
|
||||
// {
|
||||
// allowfuseread = 0;
|
||||
// }
|
||||
// if (value == safemode_fuse) {
|
||||
// if (pgm->read_byte(pgm, p, m, 0, &value) != 0)
|
||||
// {
|
||||
// allowfuseread = 0;
|
||||
// }
|
||||
// if (value == safemode_fuse)
|
||||
// {
|
||||
// fusegood = 1; /* Fuse read OK three times */
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// //Programmer does not allow fuse reading.... no point trying anymore
|
||||
// if (allowfuseread == 0)
|
||||
// {
|
||||
// return -5;
|
||||
// }
|
||||
|
||||
// if (fusegood == 0) {
|
||||
// logprintf(LOG_ERR, "AVR verify error, unable to read fuse properly.");
|
||||
// logprintf(LOG_ERR, "Programmer may not be reliable.");
|
||||
// return -1;
|
||||
// }
|
||||
// else if ((fusegood == 1)) {
|
||||
// logprintf(LOG_INFO, "AVR fuse reads as %X", safemode_lfuse);
|
||||
// }
|
||||
|
||||
|
||||
/* Read lfuse three times */
|
||||
fusegood = 2; /* If AVR device doesn't support this fuse, don't want
|
||||
to generate a verify error */
|
||||
m = p->lfusemem;
|
||||
if (m != NULL) {
|
||||
fusegood = 0; /* By default fuse is a failure */
|
||||
if (pgm->read_byte(pgm, p, m, 0, &safemode_lfuse) != 0) {
|
||||
allowfuseread = 0;
|
||||
}
|
||||
if (pgm->read_byte(pgm, p, m, 0, &value) != 0) {
|
||||
allowfuseread = 0;
|
||||
}
|
||||
if (value == safemode_lfuse) {
|
||||
if (pgm->read_byte(pgm, p, m, 0, &value) != 0) {
|
||||
allowfuseread = 0;
|
||||
}
|
||||
if (value == safemode_lfuse) {
|
||||
fusegood = 1; /* Fuse read OK three times */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Programmer does not allow fuse reading.... no point trying anymore
|
||||
if (allowfuseread == 0) {
|
||||
return -5;
|
||||
}
|
||||
|
||||
|
||||
if (fusegood == 0) {
|
||||
logprintf(LOG_ERR, "AVR verify error, unable to read lfuse properly.");
|
||||
logprintf(LOG_ERR, "Programmer may not be reliable.");
|
||||
return -1;
|
||||
}
|
||||
else if (fusegood == 1) {
|
||||
logprintf(LOG_INFO, "AVR lfuse reads as %X", safemode_lfuse);
|
||||
}
|
||||
|
||||
/* Read hfuse three times */
|
||||
fusegood = 2; /* If AVR device doesn't support this fuse, don't want
|
||||
to generate a verify error */
|
||||
m = p->hfusemem;
|
||||
if (m != NULL) {
|
||||
fusegood = 0; /* By default fuse is a failure */
|
||||
if (pgm->read_byte(pgm, p, m, 0, &safemode_hfuse) != 0) {
|
||||
allowfuseread = 0;
|
||||
}
|
||||
if (pgm->read_byte(pgm, p, m, 0, &value) != 0) {
|
||||
allowfuseread = 0;
|
||||
}
|
||||
if (value == safemode_hfuse) {
|
||||
if (pgm->read_byte(pgm, p, m, 0, &value) != 0) {
|
||||
allowfuseread = 0;
|
||||
}
|
||||
if (value == safemode_hfuse) {
|
||||
fusegood = 1; /* Fuse read OK three times */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Programmer does not allow fuse reading.... no point trying anymore
|
||||
if (allowfuseread == 0) {
|
||||
return -5;
|
||||
}
|
||||
|
||||
if (fusegood == 0) {
|
||||
logprintf(LOG_ERR, "AVR verify error, unable to read hfuse properly.");
|
||||
logprintf(LOG_ERR, "Programmer may not be reliable.");
|
||||
return -1;
|
||||
}
|
||||
else if (fusegood == 1) {
|
||||
logprintf(LOG_INFO, "AVR hfuse reads as %X", safemode_lfuse);
|
||||
}
|
||||
|
||||
/* Read efuse three times */
|
||||
fusegood = 2; /* If AVR device doesn't support this fuse, don't want
|
||||
to generate a verify error */
|
||||
// m = avr_locate_mem(p, "efuse");
|
||||
// if (m != NULL) {
|
||||
// fusegood = 0; /* By default fuse is a failure */
|
||||
// if (pgm->read_byte(pgm, p, m, 0, &safemode_efuse) != 0)
|
||||
// {
|
||||
// allowfuseread = 0;
|
||||
// }
|
||||
// if (pgm->read_byte(pgm, p, m, 0, &value) != 0)
|
||||
// {
|
||||
// allowfuseread = 0;
|
||||
// }
|
||||
// if (value == safemode_efuse) {
|
||||
// if (pgm->read_byte(pgm, p, m, 0, &value) != 0)
|
||||
// {
|
||||
// allowfuseread = 0;
|
||||
// }
|
||||
// if (value == safemode_efuse){
|
||||
// fusegood = 1; /* Fuse read OK three times */
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// //Programmer does not allow fuse reading.... no point trying anymore
|
||||
// if (allowfuseread == 0)
|
||||
// {
|
||||
// return -5;
|
||||
// }
|
||||
|
||||
// if (fusegood == 0) {
|
||||
// logprintf(LOG_ERR, "AVR verify error, unable to read efuse properly.");
|
||||
// logprintf(LOG_ERR, "Programmer may not be reliable.");
|
||||
// return -1;
|
||||
// }
|
||||
// else if ((fusegood == 1)) {
|
||||
// logprintf(LOG_INFO, "AVR efuse reads as %X", safemode_lfuse);
|
||||
// }
|
||||
|
||||
*lfuse = safemode_lfuse;
|
||||
*hfuse = safemode_hfuse;
|
||||
*efuse = safemode_efuse;
|
||||
*fuse = safemode_fuse;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* This routine will store the current values pointed to by lfuse,
|
||||
* hfuse, and efuse into an internal buffer in this routine when save
|
||||
* is set to 1. When save is 0 (or not 1 really) it will copy the
|
||||
* values from the internal buffer into the locations pointed to be
|
||||
* lfuse, hfuse, and efuse. This allows you to change the fuse bits if
|
||||
* needed from another routine (ie: have it so if user requests fuse
|
||||
* bits are changed, the requested value is now verified
|
||||
*/
|
||||
int safemode_memfuses (int save, unsigned char * lfuse, unsigned char * hfuse,
|
||||
unsigned char * efuse, unsigned char * fuse)
|
||||
{
|
||||
static unsigned char safemode_lfuse = 0xff;
|
||||
static unsigned char safemode_hfuse = 0xff;
|
||||
static unsigned char safemode_efuse = 0xff;
|
||||
static unsigned char safemode_fuse = 0xff;
|
||||
|
||||
switch (save) {
|
||||
|
||||
/* Save the fuses as safemode setting */
|
||||
case 1:
|
||||
safemode_lfuse = *lfuse;
|
||||
safemode_hfuse = *hfuse;
|
||||
safemode_efuse = *efuse;
|
||||
safemode_fuse = *fuse;
|
||||
|
||||
break;
|
||||
/* Read back the fuses */
|
||||
default:
|
||||
*lfuse = safemode_lfuse;
|
||||
*hfuse = safemode_hfuse;
|
||||
*efuse = safemode_efuse;
|
||||
*fuse = safemode_fuse;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* avrdude - A Downloader/Uploader for AVR device programmers
|
||||
* avrdude is Copyright (C) 2000-2004 Brian S. Dean <bsd@bsdhome.com>
|
||||
*
|
||||
* This file: Copyright (C) 2005 Colin O'Flynn <coflynn@newae.com>
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
|
||||
#ifndef safemode_h
|
||||
#define safemode_h
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Writes the specified fuse in fusename (can be "lfuse", "hfuse", or "efuse") and verifies it. Will try up to tries
|
||||
amount of times before giving up */
|
||||
int safemode_writefuse (unsigned char fuse, const char * fusename, PROGRAMMER * pgm, AVRPART * p, int tries);
|
||||
|
||||
/* Reads the fuses three times, checking that all readings are the same. This will ensure that the before values aren't in error! */
|
||||
int safemode_readfuses (unsigned char * lfuse, unsigned char * hfuse, unsigned char * efuse, unsigned char * fuse, PROGRAMMER * pgm, AVRPART * p);
|
||||
|
||||
/* This routine will store the current values pointed to by lfuse, hfuse, and efuse into an internal buffer in this routine
|
||||
when save is set to 1. When save is 0 (or not 1 really) it will copy the values from the internal buffer into the locations
|
||||
pointed to be lfuse, hfuse, and efuse. This allows you to change the fuse bits if needed from another routine (ie: have it so
|
||||
if user requests fuse bits are changed, the requested value is now verified */
|
||||
int safemode_memfuses (int save, unsigned char * lfuse, unsigned char * hfuse, unsigned char * efuse, unsigned char * fuse);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* safemode_h */
|
||||
@@ -0,0 +1,285 @@
|
||||
/*
|
||||
* avrdude - A Downloader/Uploader for AVR device programmers
|
||||
* Copyright (C) 2003-2004 Theodore A. Roth <troth@openavr.org>
|
||||
* Copyright (C) 2006 Joerg Wunsch <j@uriah.heep.sax.de>
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* $Id: ser_posix.c 1294 2014-03-12 23:03:18Z joerg_wunsch $ */
|
||||
|
||||
/*
|
||||
* Posix serial interface for avrdude.
|
||||
*/
|
||||
|
||||
#ifndef _WIN32
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <termios.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "../pilight/core/log.h"
|
||||
#include "../pilight/core/mem.h"
|
||||
#include "avrdude.h"
|
||||
#include "serial.h"
|
||||
|
||||
long serial_recv_timeout = 5000; /* ms */
|
||||
|
||||
static struct termios original_termios;
|
||||
static int saved_original_termios;
|
||||
|
||||
static int ser_setspeed(union filedescriptor *fd, long baud) {
|
||||
int rc = 0;
|
||||
struct termios termios;
|
||||
speed_t speed = baud;
|
||||
|
||||
if(!isatty(fd->ifd)) {
|
||||
return -ENOTTY;
|
||||
}
|
||||
|
||||
/*
|
||||
* initialize terminal modes
|
||||
*/
|
||||
if(tcgetattr(fd->ifd, &termios) < 0) {
|
||||
logprintf(LOG_ERR, "ser_setspeed(): tcgetattr() failed");
|
||||
return -errno;
|
||||
}
|
||||
|
||||
/*
|
||||
* copy termios for ser_close if we haven't already
|
||||
*/
|
||||
if(!saved_original_termios++) {
|
||||
original_termios = termios;
|
||||
}
|
||||
|
||||
termios.c_iflag = IGNBRK;
|
||||
termios.c_oflag = 0;
|
||||
termios.c_lflag = 0;
|
||||
termios.c_cflag = (CS8 | CREAD | CLOCAL);
|
||||
termios.c_cc[VMIN] = 1;
|
||||
termios.c_cc[VTIME] = 0;
|
||||
|
||||
cfsetospeed(&termios, speed);
|
||||
cfsetispeed(&termios, speed);
|
||||
|
||||
if(tcsetattr(fd->ifd, TCSANOW, &termios) < 0) {
|
||||
logprintf(LOG_ERR, "%s: ser_setspeed(): tcsetattr() failed");
|
||||
return -errno;
|
||||
}
|
||||
|
||||
/*
|
||||
* Everything is now set up for a local line without modem control
|
||||
* or flow control, so clear O_NONBLOCK again.
|
||||
*/
|
||||
rc = fcntl(fd->ifd, F_GETFL, 0);
|
||||
if(rc != -1) {
|
||||
fcntl(fd->ifd, F_SETFL, rc & ~O_NONBLOCK);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ser_set_dtr_rts(union filedescriptor *fdp, int is_on) {
|
||||
unsigned int ctl = 0;
|
||||
|
||||
if(ioctl(fdp->ifd, TIOCMGET, &ctl) < 0) {
|
||||
// perror("ioctl(\"TIOCMGET\")");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(is_on > 0) {
|
||||
/* Set DTR and RTS */
|
||||
ctl |= (TIOCM_DTR | TIOCM_RTS);
|
||||
} else {
|
||||
/* Clear DTR and RTS */
|
||||
ctl &= ~(TIOCM_DTR | TIOCM_RTS);
|
||||
}
|
||||
|
||||
if(ioctl(fdp->ifd, TIOCMSET, &ctl) < 0) {
|
||||
// perror("ioctl(\"TIOCMSET\")");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ser_open(char * port, union pinfo pinfo, union filedescriptor *fdp) {
|
||||
int rc = 0, fd = 0;
|
||||
|
||||
/*
|
||||
* open the serial port
|
||||
*/
|
||||
if((fd = open(port, O_RDWR | O_NOCTTY | O_NONBLOCK)) < 0) {
|
||||
logprintf(LOG_ERR, "ser_open(): can't open device \"%s\": %s", port, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
fdp->ifd = fd;
|
||||
|
||||
/*
|
||||
* set serial line attributes
|
||||
*/
|
||||
if((rc = ser_setspeed(fdp, pinfo.baud)) > 0) {
|
||||
logprintf(LOG_ERR, "ser_open(): can't set attributes for device \"%s\": %s", port, strerror(-rc));
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void ser_close(union filedescriptor *fd) {
|
||||
/*
|
||||
* restore original termios settings from ser_open
|
||||
*/
|
||||
if(saved_original_termios > 0) {
|
||||
if(tcsetattr(fd->ifd, TCSANOW | TCSADRAIN, &original_termios) > 0) {
|
||||
logprintf(LOG_ERR, "ser_close(): can't reset attributes for device: %s", strerror(errno));
|
||||
}
|
||||
saved_original_termios = 0;
|
||||
}
|
||||
|
||||
close(fd->ifd);
|
||||
}
|
||||
|
||||
|
||||
static int ser_send(union filedescriptor *fd, unsigned char * buf, size_t buflen) {
|
||||
int rc = 0;
|
||||
unsigned char *p = buf;
|
||||
size_t len = buflen;
|
||||
|
||||
if(len <= 0)
|
||||
return 0;
|
||||
|
||||
while(len) {
|
||||
rc = write(fd->ifd, p, (len > 1024) ? 1024 : len);
|
||||
if(rc < 0) {
|
||||
logprintf(LOG_ERR, "ser_send(): write error: %s", strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
p += rc;
|
||||
len -= rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int ser_recv(union filedescriptor *fd, unsigned char * buf, size_t buflen) {
|
||||
struct timeval timeout, to2;
|
||||
fd_set rfds;
|
||||
int nfds = 0, rc = 0;
|
||||
unsigned char *p = buf;
|
||||
size_t len = 0;
|
||||
|
||||
timeout.tv_sec = serial_recv_timeout / 1000L;
|
||||
timeout.tv_usec = (serial_recv_timeout % 1000L) * 1000;
|
||||
to2 = timeout;
|
||||
|
||||
while(len < buflen) {
|
||||
reselect:
|
||||
FD_ZERO(&rfds);
|
||||
FD_SET(fd->ifd, &rfds);
|
||||
|
||||
nfds = select(fd->ifd + 1, &rfds, NULL, NULL, &to2);
|
||||
if(nfds == 0) {
|
||||
logprintf(LOG_ERR, "ser_recv(): programmer is not responding");
|
||||
return -1;
|
||||
} else if(nfds == -1) {
|
||||
if(errno == EINTR || errno == EAGAIN) {
|
||||
logprintf(LOG_ERR, "ser_recv(): programmer is not responding, reselecting");
|
||||
goto reselect;
|
||||
} else {
|
||||
logprintf(LOG_ERR, "ser_recv(): select(): %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
rc = read(fd->ifd, p, (buflen - len > 1024) ? 1024 : buflen - len);
|
||||
if(rc < 0) {
|
||||
logprintf(LOG_ERR, "ser_recv(): read error: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
p += rc;
|
||||
len += rc;
|
||||
}
|
||||
|
||||
p = buf;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int ser_drain(union filedescriptor *fd, int display) {
|
||||
struct timeval timeout;
|
||||
fd_set rfds;
|
||||
int nfds = 0, rc = 0;
|
||||
unsigned char buf;
|
||||
|
||||
timeout.tv_sec = 0;
|
||||
timeout.tv_usec = 250000;
|
||||
|
||||
while(1) {
|
||||
FD_ZERO(&rfds);
|
||||
FD_SET(fd->ifd, &rfds);
|
||||
|
||||
reselect:
|
||||
nfds = select(fd->ifd + 1, &rfds, NULL, NULL, &timeout);
|
||||
if(nfds == 0) {
|
||||
break;
|
||||
} else if(nfds == -1) {
|
||||
if(errno == EINTR) {
|
||||
goto reselect;
|
||||
} else {
|
||||
logprintf(LOG_ERR, "ser_drain(): select(): %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
rc = read(fd->ifd, &buf, 1);
|
||||
if(rc < 0) {
|
||||
logprintf(LOG_ERR, "ser_drain(): read error: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct serial_device serial_serdev = {
|
||||
.open = ser_open,
|
||||
.setspeed = ser_setspeed,
|
||||
.close = ser_close,
|
||||
.send = ser_send,
|
||||
.recv = ser_recv,
|
||||
.drain = ser_drain,
|
||||
.set_dtr_rts = ser_set_dtr_rts,
|
||||
.flags = SERDEV_FL_CANSETSPEED,
|
||||
};
|
||||
|
||||
struct serial_device *serdev = &serial_serdev;
|
||||
|
||||
#endif /* WIN32NATIVE */
|
||||
@@ -0,0 +1,292 @@
|
||||
/*
|
||||
* avrdude - A Downloader/Uploader for AVR device programmers
|
||||
* Copyright (C) 2003-2004 Theodore A. Roth <troth@openavr.org>
|
||||
* Copyright (C) 2006 Joerg Wunsch <j@uriah.heep.sax.de>
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* $Id: ser_win32.c 1294 2014-03-12 23:03:18Z joerg_wunsch $ */
|
||||
|
||||
/*
|
||||
* Native Win32 serial interface for avrdude.
|
||||
*/
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#include <windows.h>
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "../pilight/core/log.h"
|
||||
#include "../pilight/core/mem.h"
|
||||
#include "serial.h"
|
||||
|
||||
long serial_recv_timeout = 5000; /* ms */
|
||||
|
||||
#define W32SERBUFSIZE 1024
|
||||
|
||||
static BOOL serial_w32SetTimeOut(HANDLE hComPort, DWORD timeout) {
|
||||
COMMTIMEOUTS ctmo;
|
||||
ZeroMemory (&ctmo, sizeof(COMMTIMEOUTS));
|
||||
ctmo.ReadIntervalTimeout = timeout;
|
||||
ctmo.ReadTotalTimeoutMultiplier = timeout;
|
||||
ctmo.ReadTotalTimeoutConstant = timeout;
|
||||
|
||||
return SetCommTimeouts(hComPort, &ctmo);
|
||||
}
|
||||
|
||||
static int ser_setspeed(union filedescriptor *fd, long baud) {
|
||||
DCB dcb;
|
||||
HANDLE hComPort = (HANDLE)fd->pfd;
|
||||
|
||||
ZeroMemory (&dcb, sizeof(DCB));
|
||||
dcb.DCBlength = sizeof(DCB);
|
||||
dcb.BaudRate = baud;
|
||||
dcb.fBinary = 1;
|
||||
dcb.fDtrControl = DTR_CONTROL_DISABLE;
|
||||
dcb.fRtsControl = RTS_CONTROL_DISABLE;
|
||||
dcb.ByteSize = 8;
|
||||
dcb.Parity = NOPARITY;
|
||||
dcb.StopBits = ONESTOPBIT;
|
||||
|
||||
if(!SetCommState(hComPort, &dcb)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int ser_open(char * port, union pinfo pinfo, union filedescriptor *fdp) {
|
||||
LPVOID lpMsgBuf;
|
||||
HANDLE hComPort=INVALID_HANDLE_VALUE;
|
||||
char *newname = 0;
|
||||
|
||||
if(strncasecmp(port, "com", strlen("com")) == 0) {
|
||||
// prepend "\\\\.\\" to name, required for port # >= 10
|
||||
newname = MALLOC(strlen("\\\\.\\") + strlen(port) + 1);
|
||||
|
||||
if(newname == NULL) {
|
||||
fprintf(stderr, "out of memory\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
strcpy(newname, "\\\\.\\");
|
||||
strcat(newname, port);
|
||||
|
||||
port = newname;
|
||||
}
|
||||
|
||||
hComPort = CreateFile(port, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
|
||||
if(hComPort == INVALID_HANDLE_VALUE) {
|
||||
FormatMessage(
|
||||
FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
||||
FORMAT_MESSAGE_FROM_SYSTEM |
|
||||
FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
NULL,
|
||||
GetLastError(),
|
||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
|
||||
(LPTSTR) &lpMsgBuf,
|
||||
0,
|
||||
NULL);
|
||||
logprintf(LOG_ERR, "ser_open(): can't open device \"%s\": %s", port, (char*)lpMsgBuf);
|
||||
if(newname != NULL) {
|
||||
FREE(newname);
|
||||
}
|
||||
LocalFree(lpMsgBuf);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(!SetupComm(hComPort, W32SERBUFSIZE, W32SERBUFSIZE)) {
|
||||
CloseHandle(hComPort);
|
||||
logprintf(LOG_ERR, "ser_open(): can't set buffers for \"%s\"", port);
|
||||
if(newname != NULL) {
|
||||
FREE(newname);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
fdp->pfd = (void *)hComPort;
|
||||
if(ser_setspeed(fdp, pinfo.baud) != 0) {
|
||||
CloseHandle(hComPort);
|
||||
logprintf(LOG_ERR, "ser_open(): can't set com-state for \"%s\"", port);
|
||||
if(newname != NULL) {
|
||||
FREE(newname);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(!serial_w32SetTimeOut(hComPort, 0)) {
|
||||
CloseHandle(hComPort);
|
||||
logprintf(LOG_ERR, "ser_open(): can't set initial timeout for \"%s\"", port);
|
||||
if(newname != NULL) {
|
||||
FREE(newname);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(newname != NULL) {
|
||||
FREE(newname);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void ser_close(union filedescriptor *fd) {
|
||||
HANDLE hComPort=(HANDLE)fd->pfd;
|
||||
if(hComPort != INVALID_HANDLE_VALUE) {
|
||||
CloseHandle (hComPort);
|
||||
}
|
||||
|
||||
hComPort = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
static int ser_set_dtr_rts(union filedescriptor *fd, int is_on) {
|
||||
HANDLE hComPort=(HANDLE)fd->pfd;
|
||||
|
||||
if(is_on > 0) {
|
||||
EscapeCommFunction(hComPort, SETDTR);
|
||||
EscapeCommFunction(hComPort, SETRTS);
|
||||
} else {
|
||||
EscapeCommFunction(hComPort, CLRDTR);
|
||||
EscapeCommFunction(hComPort, CLRRTS);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int ser_send(union filedescriptor *fd, unsigned char *buf, size_t buflen) {
|
||||
size_t len = buflen;
|
||||
DWORD written;
|
||||
|
||||
HANDLE hComPort = (HANDLE)fd->pfd;
|
||||
|
||||
if(hComPort == INVALID_HANDLE_VALUE) {
|
||||
logprintf(LOG_ERR, "ser_send(): port not open");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(len <= 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
serial_w32SetTimeOut(hComPort,500);
|
||||
|
||||
if(!WriteFile(hComPort, buf, buflen, &written, NULL)) {
|
||||
logprintf(LOG_ERR, "ser_send(): write error"); // TODO
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if(written != buflen) {
|
||||
logprintf(LOG_ERR, "ser_send(): size/send mismatch");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int ser_recv(union filedescriptor *fd, unsigned char *buf, size_t buflen) {
|
||||
DWORD read;
|
||||
|
||||
HANDLE hComPort = (HANDLE)fd->pfd;
|
||||
|
||||
if(hComPort == INVALID_HANDLE_VALUE) {
|
||||
logprintf(LOG_ERR, "ser_read(): port not open");
|
||||
return -1;
|
||||
}
|
||||
|
||||
serial_w32SetTimeOut(hComPort, serial_recv_timeout);
|
||||
|
||||
if(!ReadFile(hComPort, buf, buflen, &read, NULL)) {
|
||||
LPVOID lpMsgBuf;
|
||||
FormatMessage(
|
||||
FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
||||
FORMAT_MESSAGE_FROM_SYSTEM |
|
||||
FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
NULL,
|
||||
GetLastError(),
|
||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
|
||||
(LPTSTR) &lpMsgBuf,
|
||||
0,
|
||||
NULL);
|
||||
logprintf(LOG_ERR, "ser_recv(): read error: %s", (char*)lpMsgBuf);
|
||||
LocalFree(lpMsgBuf);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* time out detected */
|
||||
if(read == 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int ser_drain(union filedescriptor *fd, int display) {
|
||||
unsigned char buf[10];
|
||||
BOOL readres;
|
||||
DWORD read;
|
||||
|
||||
HANDLE hComPort=(HANDLE)fd->pfd;
|
||||
|
||||
if(hComPort == INVALID_HANDLE_VALUE) {
|
||||
logprintf(LOG_ERR, "%s: ser_drain(): port not open");
|
||||
return -1;
|
||||
}
|
||||
|
||||
serial_w32SetTimeOut(hComPort,250);
|
||||
|
||||
while(1) {
|
||||
readres=ReadFile(hComPort, buf, 1, &read, NULL);
|
||||
if(!readres) {
|
||||
LPVOID lpMsgBuf;
|
||||
FormatMessage(
|
||||
FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
||||
FORMAT_MESSAGE_FROM_SYSTEM |
|
||||
FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
NULL,
|
||||
GetLastError(),
|
||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
|
||||
(LPTSTR) &lpMsgBuf,
|
||||
0,
|
||||
NULL);
|
||||
logprintf(LOG_ERR, "ser_drain(): read error: %s", (char*)lpMsgBuf);
|
||||
LocalFree(lpMsgBuf);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(read <= 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct serial_device serial_serdev = {
|
||||
.open = ser_open,
|
||||
.setspeed = ser_setspeed,
|
||||
.close = ser_close,
|
||||
.send = ser_send,
|
||||
.recv = ser_recv,
|
||||
.drain = ser_drain,
|
||||
.set_dtr_rts = ser_set_dtr_rts,
|
||||
.flags = SERDEV_FL_CANSETSPEED,
|
||||
};
|
||||
|
||||
struct serial_device *serdev = &serial_serdev;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* avrdude - A Downloader/Uploader for AVR device programmers
|
||||
* Copyright (C) 2003-2004 Theodore A. Roth <troth@openavr.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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* $Id: serial.h 1294 2014-03-12 23:03:18Z joerg_wunsch $ */
|
||||
|
||||
/* This is the API for the generic serial interface. The implementations are
|
||||
actually provided by the target dependant files:
|
||||
|
||||
ser_posix.c : posix serial interface.
|
||||
ser_win32.c : native win32 serial interface.
|
||||
|
||||
The target file will be selected at configure time. */
|
||||
|
||||
#ifndef serial_h
|
||||
#define serial_h
|
||||
|
||||
extern long serial_recv_timeout;
|
||||
union filedescriptor
|
||||
{
|
||||
int ifd;
|
||||
void *pfd;
|
||||
struct
|
||||
{
|
||||
void *handle;
|
||||
int rep; /* bulk read endpoint */
|
||||
int wep; /* bulk write endpoint */
|
||||
int eep; /* event read endpoint */
|
||||
int max_xfer; /* max transfer size */
|
||||
int use_interrupt_xfer; /* device uses interrupt transfers */
|
||||
} usb;
|
||||
};
|
||||
|
||||
union pinfo
|
||||
{
|
||||
long baud;
|
||||
struct
|
||||
{
|
||||
unsigned short vid;
|
||||
unsigned short pid;
|
||||
unsigned short flags;
|
||||
#define PINFO_FL_USEHID 0x0001
|
||||
#define PINFO_FL_SILENT 0x0002 /* don't complain if not found */
|
||||
} usbinfo;
|
||||
};
|
||||
|
||||
|
||||
struct serial_device
|
||||
{
|
||||
// open should return -1 on error, other values on success
|
||||
int (*open)(char * port, union pinfo pinfo, union filedescriptor *fd);
|
||||
int (*setspeed)(union filedescriptor *fd, long baud);
|
||||
void (*close)(union filedescriptor *fd);
|
||||
|
||||
int (*send)(union filedescriptor *fd, unsigned char * buf, size_t buflen);
|
||||
int (*recv)(union filedescriptor *fd, unsigned char * buf, size_t buflen);
|
||||
int (*drain)(union filedescriptor *fd, int display);
|
||||
|
||||
int (*set_dtr_rts)(union filedescriptor *fd, int is_on);
|
||||
|
||||
int flags;
|
||||
#define SERDEV_FL_NONE 0x0000 /* no flags */
|
||||
#define SERDEV_FL_CANSETSPEED 0x0001 /* device can change speed */
|
||||
};
|
||||
|
||||
extern struct serial_device *serdev;
|
||||
extern struct serial_device serial_serdev;
|
||||
extern struct serial_device usb_serdev;
|
||||
extern struct serial_device usb_serdev_frame;
|
||||
extern struct serial_device avrdoper_serdev;
|
||||
|
||||
#define serial_open (serdev->open)
|
||||
#define serial_setspeed (serdev->setspeed)
|
||||
#define serial_close (serdev->close)
|
||||
#define serial_send (serdev->send)
|
||||
#define serial_recv (serdev->recv)
|
||||
#define serial_drain (serdev->drain)
|
||||
#define serial_set_dtr_rts (serdev->set_dtr_rts)
|
||||
|
||||
#endif /* serial_h */
|
||||
@@ -0,0 +1,624 @@
|
||||
/*
|
||||
* avrdude - A Downloader/Uploader for AVR device programmers
|
||||
* Copyright (C) 2002-2004 Brian S. Dean <bsd@bsdhome.com>
|
||||
* Copyright (C) 2008,2014 Joerg Wunsch
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* $Id: stk500.c 1294 2014-03-12 23:03:18Z joerg_wunsch $ */
|
||||
|
||||
/*
|
||||
* avrdude interface for Atmel STK500 programmer
|
||||
*
|
||||
* Note: most commands use the "universal command" feature of the
|
||||
* programmer in a "pass through" mode, exceptions are "program
|
||||
* enable", "paged read", and "paged write".
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "../pilight/core/log.h"
|
||||
#include "avrdude.h"
|
||||
#include "avr.h"
|
||||
#include "pgm.h"
|
||||
#include "stk500.h"
|
||||
#include "stk500_private.h"
|
||||
#include "serial.h"
|
||||
|
||||
#define STK500_XTAL 7372800U
|
||||
#define MAX_SYNC_ATTEMPTS 1
|
||||
|
||||
struct pdata {
|
||||
unsigned char ext_addr_byte; /* Record ext-addr byte set in the target device (if used) */
|
||||
};
|
||||
|
||||
#define PDATA(pgm) ((struct pdata *)(pgm->cookie))
|
||||
|
||||
static int stk500_send(PROGRAMMER *pgm, unsigned char *buf, size_t len) {
|
||||
return serial_send(&pgm->fd, buf, len);
|
||||
}
|
||||
|
||||
static int stk500_recv(PROGRAMMER *pgm, unsigned char *buf, size_t len) {
|
||||
if(serial_recv(&pgm->fd, buf, len) < 0) {
|
||||
logprintf(LOG_ERR, "stk500_recv(): programmer is not responding");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int stk500_drain(PROGRAMMER *pgm, int display) {
|
||||
return serial_drain(&pgm->fd, display);
|
||||
}
|
||||
|
||||
int stk500_getsync(PROGRAMMER * pgm) {
|
||||
unsigned char buf[32], resp[32];
|
||||
int attempt = 0;
|
||||
|
||||
/*
|
||||
* get in sync */
|
||||
buf[0] = Cmnd_STK_GET_SYNC;
|
||||
buf[1] = Sync_CRC_EOP;
|
||||
|
||||
/*
|
||||
* First send and drain a few times to get rid of line noise
|
||||
*/
|
||||
stk500_send(pgm, buf, 2);
|
||||
stk500_drain(pgm, 0);
|
||||
stk500_send(pgm, buf, 2);
|
||||
stk500_drain(pgm, 0);
|
||||
|
||||
for(attempt = 0; attempt < MAX_SYNC_ATTEMPTS; attempt++) {
|
||||
stk500_send(pgm, buf, 2);
|
||||
stk500_recv(pgm, resp, 1);
|
||||
if(resp[0] == Resp_STK_INSYNC){
|
||||
break;
|
||||
}
|
||||
logprintf(LOG_ERR, "stk500_getsync() attempt %d of %d: not in sync: resp=0x%02x",
|
||||
attempt + 1, MAX_SYNC_ATTEMPTS, resp[0]);
|
||||
}
|
||||
if(attempt == MAX_SYNC_ATTEMPTS) {
|
||||
stk500_drain(pgm, 0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(stk500_recv(pgm, resp, 1) < 0) {
|
||||
return -1;
|
||||
}
|
||||
if(resp[0] != Resp_STK_OK) {
|
||||
logprintf(LOG_ERR, "stk500_getsync(): can't communicate with device: resp=0x%02x", resp[0]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* transmit an AVR device command and return the results; 'cmd' and
|
||||
* 'res' must point to at least a 4 byte data buffer
|
||||
*/
|
||||
static int stk500_cmd(PROGRAMMER *pgm, const unsigned char *cmd, unsigned char *res) {
|
||||
unsigned char buf[32];
|
||||
|
||||
buf[0] = Cmnd_STK_UNIVERSAL;
|
||||
buf[1] = cmd[0];
|
||||
buf[2] = cmd[1];
|
||||
buf[3] = cmd[2];
|
||||
buf[4] = cmd[3];
|
||||
buf[5] = Sync_CRC_EOP;
|
||||
|
||||
stk500_send(pgm, buf, 6);
|
||||
|
||||
if(stk500_recv(pgm, buf, 1) < 0) {
|
||||
return -1;
|
||||
}
|
||||
if(buf[0] != Resp_STK_INSYNC) {
|
||||
logprintf(LOG_ERR, "stk500_cmd(): programmer is out of sync");
|
||||
return -1;
|
||||
}
|
||||
|
||||
res[0] = cmd[1];
|
||||
res[1] = cmd[2];
|
||||
res[2] = cmd[3];
|
||||
if(stk500_recv(pgm, &res[3], 1) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(stk500_recv(pgm, buf, 1) < 0) {
|
||||
return -1;
|
||||
}
|
||||
if(buf[0] != Resp_STK_OK) {
|
||||
logprintf(LOG_ERR, "stk500_cmd(): protocol error");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* issue the 'chip erase' command to the AVR device
|
||||
*/
|
||||
static int stk500_chip_erase(PROGRAMMER *pgm, AVRPART *p) {
|
||||
unsigned char cmd[4];
|
||||
unsigned char res[4];
|
||||
|
||||
if(pgm->cmd == NULL) {
|
||||
logprintf(LOG_ERR, "Error: %s programmer uses stk500_chip_erase() but does not provide a cmd() method.", pgm->type);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(p->op[AVR_OP_CHIP_ERASE] == NULL) {
|
||||
logprintf(LOG_ERR, "chip erase instruction not defined for part \"%s\"", p->desc);
|
||||
return -1;
|
||||
}
|
||||
|
||||
pgm->pgm_led(pgm, ON);
|
||||
|
||||
memset(cmd, 0, sizeof(cmd));
|
||||
|
||||
avr_set_bits(p->op[AVR_OP_CHIP_ERASE], cmd);
|
||||
pgm->cmd(pgm, cmd, res);
|
||||
usleep(p->chip_erase_delay);
|
||||
pgm->initialize(pgm, p);
|
||||
|
||||
pgm->pgm_led(pgm, OFF);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* issue the 'program enable' command to the AVR device
|
||||
*/
|
||||
static int stk500_program_enable(PROGRAMMER *pgm, AVRPART *p) {
|
||||
unsigned char buf[16];
|
||||
int tries=0;
|
||||
|
||||
retry:
|
||||
|
||||
tries++;
|
||||
|
||||
buf[0] = Cmnd_STK_ENTER_PROGMODE;
|
||||
buf[1] = Sync_CRC_EOP;
|
||||
|
||||
stk500_send(pgm, buf, 2);
|
||||
if(stk500_recv(pgm, buf, 1) < 0) {
|
||||
return -1;
|
||||
}
|
||||
if(buf[0] == Resp_STK_NOSYNC) {
|
||||
if(tries > 33) {
|
||||
logprintf(LOG_ERR, "stk500_program_enable(): can't get into sync");
|
||||
return -1;
|
||||
}
|
||||
if(stk500_getsync(pgm) < 0) {
|
||||
return -1;
|
||||
}
|
||||
goto retry;
|
||||
} else if(buf[0] != Resp_STK_INSYNC) {
|
||||
logprintf(LOG_ERR, "%s: stk500_program_enable(): protocol error, expect=0x%02x, resp=0x%02x\n", Resp_STK_INSYNC, buf[0]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(stk500_recv(pgm, buf, 1) < 0)
|
||||
return -1;
|
||||
if(buf[0] == Resp_STK_OK) {
|
||||
return 0;
|
||||
} else if(buf[0] == Resp_STK_NODEVICE) {
|
||||
logprintf(LOG_ERR, "stk500_program_enable(): no device");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(buf[0] == Resp_STK_FAILED) {
|
||||
logprintf(LOG_ERR, "stk500_program_enable(): failed to enter programming mode");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
logprintf(LOG_ERR, "stk500_program_enable(): unknown response=0x%02x", buf[0]);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* initialize the AVR device and prepare it to accept commands
|
||||
*/
|
||||
static int stk500_initialize(PROGRAMMER *pgm, AVRPART *p) {
|
||||
unsigned char buf[32];
|
||||
AVRMEM *m = NULL;
|
||||
int tries = 0;
|
||||
|
||||
tries = 0;
|
||||
|
||||
retry:
|
||||
tries++;
|
||||
|
||||
memset(buf, 0, sizeof(buf));
|
||||
|
||||
/*
|
||||
* set device programming parameters
|
||||
*/
|
||||
buf[0] = Cmnd_STK_SET_DEVICE;
|
||||
buf[1] = p->stk500_devcode;
|
||||
buf[2] = 0; /* device revision */
|
||||
|
||||
if((p->flags & AVRPART_SERIALOK) && (p->flags & AVRPART_PARALLELOK)) {
|
||||
buf[3] = 0; /* device supports parallel and serial programming */
|
||||
} else {
|
||||
buf[3] = 1; /* device supports parallel only */
|
||||
}
|
||||
|
||||
if(p->flags & AVRPART_PARALLELOK) {
|
||||
if(p->flags & AVRPART_PSEUDOPARALLEL) {
|
||||
buf[4] = 0; /* pseudo parallel interface */
|
||||
} else {
|
||||
buf[4] = 1; /* full parallel interface */
|
||||
}
|
||||
}
|
||||
|
||||
buf[5] = 1; /* polling supported - XXX need this in config file */
|
||||
buf[6] = 1; /* programming is self-timed - XXX need in config file */
|
||||
buf[7] = 0;
|
||||
buf[8] = 0;
|
||||
buf[8] += p->lfusemem->size;
|
||||
buf[8] += p->hfusemem->size;
|
||||
|
||||
m = p->flashmem;
|
||||
if(m != NULL) {
|
||||
buf[9] = m->readback[0];
|
||||
buf[10] = m->readback[1];
|
||||
if(m->paged == 1) {
|
||||
buf[13] = (m->page_size >> 8) & 0x00ff;
|
||||
buf[14] = m->page_size & 0x00ff;
|
||||
}
|
||||
buf[17] = (m->size >> 24) & 0xff;
|
||||
buf[18] = (m->size >> 16) & 0xff;
|
||||
buf[19] = (m->size >> 8) & 0xff;
|
||||
buf[20] = m->size & 0xff;
|
||||
}
|
||||
|
||||
buf[11] = 0xff;
|
||||
buf[12] = 0xff;
|
||||
buf[15] = 0;
|
||||
buf[16] = 0;
|
||||
buf[21] = Sync_CRC_EOP;
|
||||
|
||||
stk500_send(pgm, buf, 22);
|
||||
if(stk500_recv(pgm, buf, 1) < 0) {
|
||||
return -1;
|
||||
}
|
||||
if(buf[0] == Resp_STK_NOSYNC) {
|
||||
logprintf(LOG_ERR, "stk500_initialize(): programmer not in sync, resp=0x%02x", buf[0]);
|
||||
if(tries > 33) {
|
||||
return -1;
|
||||
}
|
||||
if(stk500_getsync(pgm) < 0) {
|
||||
return -1;
|
||||
}
|
||||
goto retry;
|
||||
} else if(buf[0] != Resp_STK_INSYNC) {
|
||||
logprintf(LOG_ERR, "stk500_initialize(): (a) protocol error, expect=0x%02x, resp=0x%02x", Resp_STK_INSYNC, buf[0]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(stk500_recv(pgm, buf, 1) < 0) {
|
||||
return -1;
|
||||
}
|
||||
if(buf[0] != Resp_STK_OK) {
|
||||
logprintf(LOG_ERR, "stk500_initialize(): (b) protocol error, expect=0x%02x, resp=0x%02x", Resp_STK_OK, buf[0]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return pgm->program_enable(pgm, p);
|
||||
}
|
||||
|
||||
static int stk500_loadaddr(PROGRAMMER *pgm, AVRMEM *mem, unsigned int addr) {
|
||||
unsigned char buf[16];
|
||||
int tries = 0;
|
||||
unsigned char ext_byte;
|
||||
OPCODE * lext;
|
||||
|
||||
tries = 0;
|
||||
|
||||
retry:
|
||||
tries++;
|
||||
|
||||
/* To support flash > 64K words the correct Extended Address Byte is needed */
|
||||
lext = mem->op[AVR_OP_LOAD_EXT_ADDR];
|
||||
if(lext != NULL) {
|
||||
ext_byte = (addr >> 16) & 0xff;
|
||||
if(ext_byte != PDATA(pgm)->ext_addr_byte) {
|
||||
/* Either this is the first addr load, or a 64K word boundary is
|
||||
* crossed, so set the ext addr byte */
|
||||
avr_set_bits(lext, buf);
|
||||
avr_set_addr(lext, buf, addr);
|
||||
stk500_cmd(pgm, buf, buf);
|
||||
PDATA(pgm)->ext_addr_byte = ext_byte;
|
||||
}
|
||||
}
|
||||
|
||||
buf[0] = Cmnd_STK_LOAD_ADDRESS;
|
||||
buf[1] = addr & 0xff;
|
||||
buf[2] = (addr >> 8) & 0xff;
|
||||
buf[3] = Sync_CRC_EOP;
|
||||
|
||||
stk500_send(pgm, buf, 4);
|
||||
|
||||
if(stk500_recv(pgm, buf, 1) < 0) {
|
||||
return -1;
|
||||
}
|
||||
if(buf[0] == Resp_STK_NOSYNC) {
|
||||
if(tries > 33) {
|
||||
logprintf(LOG_ERR, "stk500_loadaddr(): can't get into sync");
|
||||
return -1;
|
||||
}
|
||||
if(stk500_getsync(pgm) < 0) {
|
||||
return -1;
|
||||
}
|
||||
goto retry;
|
||||
} else if(buf[0] != Resp_STK_INSYNC) {
|
||||
logprintf(LOG_ERR, "stk500_loadaddr(): (a) protocol error, expect=0x%02x, resp=0x%02x", Resp_STK_INSYNC, buf[0]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(stk500_recv(pgm, buf, 1) < 0) {
|
||||
return -1;
|
||||
}
|
||||
if(buf[0] == Resp_STK_OK) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
logprintf(LOG_ERR, "loadaddr(): (b) protocol error, expect=0x%02x, resp=0x%02x", Resp_STK_INSYNC, buf[0]);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
static int stk500_paged_write(PROGRAMMER *pgm, AVRPART *p, AVRMEM *m,
|
||||
unsigned int page_size,
|
||||
unsigned int addr, unsigned int n_bytes) {
|
||||
|
||||
unsigned char buf[page_size + 16];
|
||||
int memtype = 0, a_div = 0, block_size = 0, tries = 0;
|
||||
unsigned int n = 0, i = 0;
|
||||
|
||||
if(strcmp(m->desc, "flash") == 0) {
|
||||
memtype = 'F';
|
||||
} else if(strcmp(m->desc, "eeprom") == 0) {
|
||||
memtype = 'E';
|
||||
} else {
|
||||
return -2;
|
||||
}
|
||||
|
||||
if((m->op[AVR_OP_LOADPAGE_LO]) || (m->op[AVR_OP_READ_LO])) {
|
||||
a_div = 2;
|
||||
} else {
|
||||
a_div = 1;
|
||||
}
|
||||
|
||||
n = addr + n_bytes;
|
||||
|
||||
for(; addr < n; addr += block_size) {
|
||||
if(n - addr < page_size) {
|
||||
block_size = n - addr;
|
||||
} else {
|
||||
block_size = page_size;
|
||||
}
|
||||
tries = 0;
|
||||
retry:
|
||||
tries++;
|
||||
stk500_loadaddr(pgm, m, addr/a_div);
|
||||
|
||||
/* build command block and avoid multiple send commands as it leads to a crash
|
||||
of the silabs usb serial driver on mac os x */
|
||||
i = 0;
|
||||
buf[i++] = Cmnd_STK_PROG_PAGE;
|
||||
buf[i++] = (block_size >> 8) & 0xff;
|
||||
buf[i++] = block_size & 0xff;
|
||||
buf[i++] = memtype;
|
||||
|
||||
memcpy(&buf[i], &m->buf[addr], block_size);
|
||||
|
||||
i += block_size;
|
||||
buf[i++] = Sync_CRC_EOP;
|
||||
stk500_send(pgm, buf, i);
|
||||
|
||||
if(stk500_recv(pgm, buf, 1) < 0) {
|
||||
return -1;
|
||||
}
|
||||
if(buf[0] == Resp_STK_NOSYNC) {
|
||||
if(tries > 33) {
|
||||
logprintf(LOG_ERR, "stk500_paged_write(): can't get into sync", progname);
|
||||
return -3;
|
||||
}
|
||||
if(stk500_getsync(pgm) < 0) {
|
||||
return -1;
|
||||
}
|
||||
goto retry;
|
||||
} else if(buf[0] != Resp_STK_INSYNC) {
|
||||
logprintf(LOG_ERR, "stk500_paged_write(): (a) protocol error, expect=0x%02x, resp=0x%02x", Resp_STK_INSYNC, buf[0]);
|
||||
return -4;
|
||||
}
|
||||
|
||||
if(stk500_recv(pgm, buf, 1) < 0) {
|
||||
return -1;
|
||||
}
|
||||
if(buf[0] != Resp_STK_OK) {
|
||||
logprintf(LOG_ERR, "stk500_paged_write(): (a) protocol error, expect=0x%02x, resp=0x%02x", Resp_STK_INSYNC, buf[0]);
|
||||
return -5;
|
||||
}
|
||||
}
|
||||
|
||||
return n_bytes;
|
||||
}
|
||||
|
||||
static int stk500_paged_load(PROGRAMMER *pgm, AVRPART *p, AVRMEM *m,
|
||||
unsigned int page_size,
|
||||
unsigned int addr, unsigned int n_bytes) {
|
||||
unsigned char buf[16];
|
||||
int memtype = 0, a_div = 0, tries = 0, block_size = 0;
|
||||
unsigned int n = 0;
|
||||
|
||||
if(strcmp(m->desc, "flash") == 0) {
|
||||
memtype = 'F';
|
||||
} else if(strcmp(m->desc, "eeprom") == 0) {
|
||||
memtype = 'E';
|
||||
} else {
|
||||
return -2;
|
||||
}
|
||||
|
||||
if((m->op[AVR_OP_LOADPAGE_LO]) || (m->op[AVR_OP_READ_LO])) {
|
||||
a_div = 2;
|
||||
} else {
|
||||
a_div = 1;
|
||||
}
|
||||
|
||||
n = addr + n_bytes;
|
||||
for(; addr < n; addr += block_size) {
|
||||
if(n - addr < page_size) {
|
||||
block_size = n - addr;
|
||||
} else {
|
||||
block_size = page_size;
|
||||
}
|
||||
|
||||
tries = 0;
|
||||
retry:
|
||||
tries++;
|
||||
stk500_loadaddr(pgm, m, addr/a_div);
|
||||
buf[0] = Cmnd_STK_READ_PAGE;
|
||||
buf[1] = (block_size >> 8) & 0xff;
|
||||
buf[2] = block_size & 0xff;
|
||||
buf[3] = memtype;
|
||||
buf[4] = Sync_CRC_EOP;
|
||||
stk500_send(pgm, buf, 5);
|
||||
|
||||
if(stk500_recv(pgm, buf, 1) < 0) {
|
||||
return -1;
|
||||
}
|
||||
if(buf[0] == Resp_STK_NOSYNC) {
|
||||
if(tries > 33) {
|
||||
logprintf(LOG_ERR, "stk500_paged_load(): can't get into sync");
|
||||
return -3;
|
||||
}
|
||||
if(stk500_getsync(pgm) < 0) {
|
||||
return -1;
|
||||
goto retry;
|
||||
}
|
||||
} else if(buf[0] != Resp_STK_INSYNC) {
|
||||
logprintf(LOG_ERR, "stk500_paged_load(): (a) protocol error, expect=0x%02x, resp=0x%02x", Resp_STK_INSYNC, buf[0]);
|
||||
return -4;
|
||||
}
|
||||
|
||||
if(stk500_recv(pgm, &m->buf[addr], block_size) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(stk500_recv(pgm, buf, 1) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(buf[0] != Resp_STK_OK) {
|
||||
logprintf(LOG_ERR, "stk500_paged_load(): (a) protocol error, expect=0x%02x, resp=0x%02x", Resp_STK_OK, buf[0]);
|
||||
return -5;
|
||||
}
|
||||
}
|
||||
|
||||
return n_bytes;
|
||||
}
|
||||
|
||||
|
||||
static void stk500_display(PROGRAMMER * pgm, const char * p) {
|
||||
}
|
||||
|
||||
static void stk500_disable(PROGRAMMER * pgm) {
|
||||
unsigned char buf[16];
|
||||
int tries=0;
|
||||
|
||||
retry:
|
||||
tries++;
|
||||
buf[0] = Cmnd_STK_LEAVE_PROGMODE;
|
||||
buf[1] = Sync_CRC_EOP;
|
||||
stk500_send(pgm, buf, 2);
|
||||
|
||||
if(stk500_recv(pgm, buf, 1) < 0) {
|
||||
return;
|
||||
}
|
||||
if(buf[0] == Resp_STK_NOSYNC) {
|
||||
if(tries > 33) {
|
||||
logprintf(LOG_ERR, "stk500_disable(): can't get into sync");
|
||||
return;
|
||||
}
|
||||
if(stk500_getsync(pgm) < 0) {
|
||||
return;
|
||||
}
|
||||
goto retry;
|
||||
} else if(buf[0] != Resp_STK_INSYNC) {
|
||||
logprintf(LOG_ERR, "stk500_disable(): protocol error, expect=0x%02x, resp=0x%02x", Resp_STK_INSYNC, buf[0]);
|
||||
return;
|
||||
}
|
||||
if(stk500_recv(pgm, buf, 1) < 0) {
|
||||
return;
|
||||
}
|
||||
if(buf[0] == Resp_STK_OK) {
|
||||
return;
|
||||
} else if(buf[0] == Resp_STK_NODEVICE) {
|
||||
logprintf(LOG_ERR, "stk500_disable(): no device");
|
||||
return;
|
||||
}
|
||||
logprintf(LOG_ERR, "stk500_disable(): unknown response=0x%02x", buf[0]);
|
||||
return;
|
||||
}
|
||||
|
||||
static void stk500_enable(PROGRAMMER *pgm) {
|
||||
return;
|
||||
}
|
||||
|
||||
static void stk500_close(PROGRAMMER *pgm) {
|
||||
serial_close(&pgm->fd);
|
||||
pgm->fd.ifd = -1;
|
||||
}
|
||||
|
||||
const char stk500_desc[] = "Atmel STK500 Version 1.x firmware";
|
||||
|
||||
void stk500_initpgm(PROGRAMMER * pgm) {
|
||||
strcpy(pgm->type, "STK500");
|
||||
|
||||
/*
|
||||
* mandatory functions
|
||||
*/
|
||||
pgm->initialize = stk500_initialize;
|
||||
pgm->display = stk500_display;
|
||||
pgm->enable = stk500_enable;
|
||||
pgm->disable = stk500_disable;
|
||||
pgm->program_enable = stk500_program_enable;
|
||||
pgm->chip_erase = stk500_chip_erase;
|
||||
pgm->cmd = stk500_cmd;
|
||||
pgm->close = stk500_close;
|
||||
pgm->read_byte = avr_read_byte_default;
|
||||
pgm->write_byte = avr_write_byte_default;
|
||||
|
||||
/*
|
||||
* optional functions
|
||||
*/
|
||||
pgm->paged_write = stk500_paged_write;
|
||||
pgm->paged_load = stk500_paged_load;
|
||||
pgm->page_size = 256;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* avrdude - A Downloader/Uploader for AVR device programmers
|
||||
* Copyright (C) 2002-2004 Brian S. Dean <bsd@bsdhome.com>
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* $Id: stk500.h 1294 2014-03-12 23:03:18Z joerg_wunsch $ */
|
||||
|
||||
#ifndef stk500_h
|
||||
#define stk500_h
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern const char stk500_desc[];
|
||||
void stk500_initpgm (PROGRAMMER * pgm);
|
||||
|
||||
/* used by arduino.c to avoid duplicate code */
|
||||
int stk500_getsync(PROGRAMMER * pgm);
|
||||
int stk500_drain(PROGRAMMER * pgm, int display);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
//**** ATMEL AVR - A P P L I C A T I O N N O T E ************************
|
||||
//*
|
||||
//* Title: AVR061 - STK500 Communication Protocol
|
||||
//* Filename: command.h
|
||||
//* Version: 1.0
|
||||
//* Last updated: 09.09.2002
|
||||
//*
|
||||
//* Support E-mail: avr@atmel.com
|
||||
//*
|
||||
//**************************************************************************
|
||||
|
||||
// *****************[ STK Message constants ]***************************
|
||||
|
||||
#define STK_SIGN_ON_MESSAGE "AVR STK" // Sign on string for Cmnd_STK_GET_SIGN_ON
|
||||
|
||||
// *****************[ STK Response constants ]***************************
|
||||
|
||||
#define Resp_STK_OK 0x10 // ' '
|
||||
#define Resp_STK_FAILED 0x11 // ' '
|
||||
#define Resp_STK_UNKNOWN 0x12 // ' '
|
||||
#define Resp_STK_NODEVICE 0x13 // ' '
|
||||
#define Resp_STK_INSYNC 0x14 // ' '
|
||||
#define Resp_STK_NOSYNC 0x15 // ' '
|
||||
|
||||
#define Resp_ADC_CHANNEL_ERROR 0x16 // ' '
|
||||
#define Resp_ADC_MEASURE_OK 0x17 // ' '
|
||||
#define Resp_PWM_CHANNEL_ERROR 0x18 // ' '
|
||||
#define Resp_PWM_ADJUST_OK 0x19 // ' '
|
||||
|
||||
// *****************[ STK Special constants ]***************************
|
||||
|
||||
#define Sync_CRC_EOP 0x20 // 'SPACE'
|
||||
|
||||
// *****************[ STK Command constants ]***************************
|
||||
|
||||
#define Cmnd_STK_GET_SYNC 0x30 // ' '
|
||||
#define Cmnd_STK_GET_SIGN_ON 0x31 // ' '
|
||||
|
||||
#define Cmnd_STK_SET_PARAMETER 0x40 // ' '
|
||||
#define Cmnd_STK_GET_PARAMETER 0x41 // ' '
|
||||
#define Cmnd_STK_SET_DEVICE 0x42 // ' '
|
||||
#define Cmnd_STK_SET_DEVICE_EXT 0x45 // ' '
|
||||
|
||||
#define Cmnd_STK_ENTER_PROGMODE 0x50 // ' '
|
||||
#define Cmnd_STK_LEAVE_PROGMODE 0x51 // ' '
|
||||
#define Cmnd_STK_CHIP_ERASE 0x52 // ' '
|
||||
#define Cmnd_STK_CHECK_AUTOINC 0x53 // ' '
|
||||
#define Cmnd_STK_LOAD_ADDRESS 0x55 // ' '
|
||||
#define Cmnd_STK_UNIVERSAL 0x56 // ' '
|
||||
#define Cmnd_STK_UNIVERSAL_MULTI 0x57 // ' '
|
||||
|
||||
#define Cmnd_STK_PROG_FLASH 0x60 // ' '
|
||||
#define Cmnd_STK_PROG_DATA 0x61 // ' '
|
||||
#define Cmnd_STK_PROG_FUSE 0x62 // ' '
|
||||
#define Cmnd_STK_PROG_LOCK 0x63 // ' '
|
||||
#define Cmnd_STK_PROG_PAGE 0x64 // ' '
|
||||
#define Cmnd_STK_PROG_FUSE_EXT 0x65 // ' '
|
||||
|
||||
#define Cmnd_STK_READ_FLASH 0x70 // ' '
|
||||
#define Cmnd_STK_READ_DATA 0x71 // ' '
|
||||
#define Cmnd_STK_READ_FUSE 0x72 // ' '
|
||||
#define Cmnd_STK_READ_LOCK 0x73 // ' '
|
||||
#define Cmnd_STK_READ_PAGE 0x74 // ' '
|
||||
#define Cmnd_STK_READ_SIGN 0x75 // ' '
|
||||
#define Cmnd_STK_READ_OSCCAL 0x76 // ' '
|
||||
#define Cmnd_STK_READ_FUSE_EXT 0x77 // ' '
|
||||
#define Cmnd_STK_READ_OSCCAL_EXT 0x78 // ' '
|
||||
|
||||
// *****************[ STK Parameter constants ]***************************
|
||||
|
||||
#define Parm_STK_HW_VER 0x80 // ' ' - R
|
||||
#define Parm_STK_SW_MAJOR 0x81 // ' ' - R
|
||||
#define Parm_STK_SW_MINOR 0x82 // ' ' - R
|
||||
#define Parm_STK_LEDS 0x83 // ' ' - R/W
|
||||
#define Parm_STK_VTARGET 0x84 // ' ' - R/W
|
||||
#define Parm_STK_VADJUST 0x85 // ' ' - R/W
|
||||
#define Parm_STK_OSC_PSCALE 0x86 // ' ' - R/W
|
||||
#define Parm_STK_OSC_CMATCH 0x87 // ' ' - R/W
|
||||
#define Parm_STK_RESET_DURATION 0x88 // ' ' - R/W
|
||||
#define Parm_STK_SCK_DURATION 0x89 // ' ' - R/W
|
||||
|
||||
#define Parm_STK_BUFSIZEL 0x90 // ' ' - R/W, Range {0..255}
|
||||
#define Parm_STK_BUFSIZEH 0x91 // ' ' - R/W, Range {0..255}
|
||||
#define Parm_STK_DEVICE 0x92 // ' ' - R/W, Range {0..255}
|
||||
#define Parm_STK_PROGMODE 0x93 // ' ' - 'P' or 'S'
|
||||
#define Parm_STK_PARAMODE 0x94 // ' ' - TRUE or FALSE
|
||||
#define Parm_STK_POLLING 0x95 // ' ' - TRUE or FALSE
|
||||
#define Parm_STK_SELFTIMED 0x96 // ' ' - TRUE or FALSE
|
||||
#define Param_STK500_TOPCARD_DETECT 0x98 // ' ' - Detect top-card attached
|
||||
|
||||
// *****************[ STK status bit definitions ]***************************
|
||||
|
||||
#define Stat_STK_INSYNC 0x01 // INSYNC status bit, '1' - INSYNC
|
||||
#define Stat_STK_PROGMODE 0x02 // Programming mode, '1' - PROGMODE
|
||||
#define Stat_STK_STANDALONE 0x04 // Standalone mode, '1' - SM mode
|
||||
#define Stat_STK_RESET 0x08 // RESET button, '1' - Pushed
|
||||
#define Stat_STK_PROGRAM 0x10 // Program button, ' 1' - Pushed
|
||||
#define Stat_STK_LEDG 0x20 // Green LED status, '1' - Lit
|
||||
#define Stat_STK_LEDR 0x40 // Red LED status, '1' - Lit
|
||||
#define Stat_STK_LEDBLINK 0x80 // LED blink ON/OFF, '1' - Blink
|
||||
|
||||
|
||||
// *****************************[ End Of COMMAND.H ]**************************
|
||||
@@ -0,0 +1,68 @@
|
||||
project(libuv)
|
||||
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${PROJECT_SOURCE_DIR}/include/")
|
||||
|
||||
file(GLOB ${PROJECT_NAME}_sources "${PROJECT_SOURCE_DIR}/*.c")
|
||||
file(GLOB ${PROJECT_NAME}_headers "${PROJECT_SOURCE_DIR}/*.h")
|
||||
|
||||
if(WIN32)
|
||||
file(GLOB ${PROJECT_NAME}_sources ${${PROJECT_NAME}_sources} "${PROJECT_SOURCE_DIR}/win/*.c")
|
||||
file(GLOB ${PROJECT_NAME}_headers ${${PROJECT_NAME}_headers} "${PROJECT_SOURCE_DIR}/win/*.h")
|
||||
else()
|
||||
file(GLOB ${PROJECT_NAME}_sources ${${PROJECT_NAME}_sources} "${PROJECT_SOURCE_DIR}/unix/*.c")
|
||||
file(GLOB ${PROJECT_NAME}_headers ${${PROJECT_NAME}_headers} "${PROJECT_SOURCE_DIR}/unix/*.h")
|
||||
|
||||
# Darwin only
|
||||
list(REMOVE_ITEM ${PROJECT_NAME}_sources "${PROJECT_SOURCE_DIR}/unix/darwin-proctitle.c")
|
||||
list(REMOVE_ITEM ${PROJECT_NAME}_sources "${PROJECT_SOURCE_DIR}/unix/fsevents.c")
|
||||
list(REMOVE_ITEM ${PROJECT_NAME}_sources "${PROJECT_SOURCE_DIR}/unix/darwin.c")
|
||||
# Sun only
|
||||
list(REMOVE_ITEM ${PROJECT_NAME}_sources "${PROJECT_SOURCE_DIR}/unix/sunos.c")
|
||||
# AIX only
|
||||
list(REMOVE_ITEM ${PROJECT_NAME}_sources "${PROJECT_SOURCE_DIR}/unix/aix.c")
|
||||
# OpenBSD only
|
||||
list(REMOVE_ITEM ${PROJECT_NAME}_sources "${PROJECT_SOURCE_DIR}/unix/openbsd.c")
|
||||
# NetBSD only
|
||||
list(REMOVE_ITEM ${PROJECT_NAME}_sources "${PROJECT_SOURCE_DIR}/unix/netbsd.c")
|
||||
# Android only
|
||||
list(REMOVE_ITEM ${PROJECT_NAME}_sources "${PROJECT_SOURCE_DIR}/unix/android-ifaddrs.c")
|
||||
# Cygwin only
|
||||
list(REMOVE_ITEM ${PROJECT_NAME}_sources "${PROJECT_SOURCE_DIR}/unix/cygwin.c")
|
||||
# OS390 only
|
||||
list(REMOVE_ITEM ${PROJECT_NAME}_sources "${PROJECT_SOURCE_DIR}/unix/os390.c")
|
||||
list(REMOVE_ITEM ${PROJECT_NAME}_sources "${PROJECT_SOURCE_DIR}/unix/os390-syscalls.c")
|
||||
# General
|
||||
list(REMOVE_ITEM ${PROJECT_NAME}_sources "${PROJECT_SOURCE_DIR}/unix/no-fsevents.c")
|
||||
list(REMOVE_ITEM ${PROJECT_NAME}_sources "${PROJECT_SOURCE_DIR}/unix/no-proctitle.c")
|
||||
list(REMOVE_ITEM ${PROJECT_NAME}_sources "${PROJECT_SOURCE_DIR}/unix/procfs-exepath.c")
|
||||
# list(REMOVE_ITEM ${PROJECT_NAME}_sources "${PROJECT_SOURCE_DIR}/unix/posix-poll.c")
|
||||
|
||||
if(${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD")
|
||||
list(REMOVE_ITEM ${PROJECT_NAME}_sources "${PROJECT_SOURCE_DIR}/unix/pthread-barrier.c")
|
||||
list(REMOVE_ITEM ${PROJECT_NAME}_sources "${PROJECT_SOURCE_DIR}/unix/linux-inotify.c")
|
||||
list(REMOVE_ITEM ${PROJECT_NAME}_sources "${PROJECT_SOURCE_DIR}/unix/linux-core.c")
|
||||
list(REMOVE_ITEM ${PROJECT_NAME}_sources "${PROJECT_SOURCE_DIR}/unix/proctitle.c")
|
||||
list(REMOVE_ITEM ${PROJECT_NAME}_sources "${PROJECT_SOURCE_DIR}/unix/sunos.c")
|
||||
list(REMOVE_ITEM ${PROJECT_NAME}_sources "${PROJECT_SOURCE_DIR}/unix/kqueue.c")
|
||||
list(REMOVE_ITEM ${PROJECT_NAME}_sources "${PROJECT_SOURCE_DIR}/unix/freebsd.c")
|
||||
list(REMOVE_ITEM ${PROJECT_NAME}_sources "${PROJECT_SOURCE_DIR}/unix/sysinfo-loadavg.c")
|
||||
list(REMOVE_ITEM ${PROJECT_NAME}_sources "${PROJECT_SOURCE_DIR}/unix/sysinfo-memory.c")
|
||||
# elseif(${CMAKE_SYSTEM_NAME} MATCHES "SunOS")
|
||||
# list(REMOVE_ITEM ${PROJECT_NAME}_sources "${PROJECT_SOURCE_DIR}/unix/pthread-barrier.c")
|
||||
# list(REMOVE_ITEM ${PROJECT_NAME}_sources "${PROJECT_SOURCE_DIR}/unix/linux-inotify.c")
|
||||
# list(REMOVE_ITEM ${PROJECT_NAME}_sources "${PROJECT_SOURCE_DIR}/unix/linux-core.c")
|
||||
# list(REMOVE_ITEM ${PROJECT_NAME}_sources "${PROJECT_SOURCE_DIR}/unix/proctitle.c")
|
||||
# list(REMOVE_ITEM ${PROJECT_NAME}_sources "${PROJECT_SOURCE_DIR}/unix/kqueue.c")
|
||||
# list(REMOVE_ITEM ${PROJECT_NAME}_sources "${PROJECT_SOURCE_DIR}/unix/freebsd.c")
|
||||
else()
|
||||
list(REMOVE_ITEM ${PROJECT_NAME}_sources "${PROJECT_SOURCE_DIR}/unix/posix-poll.c")
|
||||
list(REMOVE_ITEM ${PROJECT_NAME}_sources "${PROJECT_SOURCE_DIR}/unix/posix-hrtime.c")
|
||||
list(REMOVE_ITEM ${PROJECT_NAME}_sources "${PROJECT_SOURCE_DIR}/unix/kqueue.c")
|
||||
list(REMOVE_ITEM ${PROJECT_NAME}_sources "${PROJECT_SOURCE_DIR}/unix/pthread-barrier.c")
|
||||
list(REMOVE_ITEM ${PROJECT_NAME}_sources "${PROJECT_SOURCE_DIR}/unix/freebsd.c")
|
||||
list(REMOVE_ITEM ${PROJECT_NAME}_sources "${PROJECT_SOURCE_DIR}/unix/bsd-ifaddrs.c")
|
||||
list(REMOVE_ITEM ${PROJECT_NAME}_sources "${PROJECT_SOURCE_DIR}/unix/sunos.c")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(${PROJECT_NAME}_files ${${PROJECT_NAME}_sources} ${${PROJECT_NAME}_headers} CACHE INTERNAL "")
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (c) 1995, 1999
|
||||
* Berkeley Software Design, Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY Berkeley Software Design, Inc. ``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 Berkeley Software Design, Inc. 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.
|
||||
*
|
||||
* BSDI ifaddrs.h,v 2.5 2000/02/23 14:51:59 dab Exp
|
||||
*/
|
||||
|
||||
#ifndef _IFADDRS_H_
|
||||
#define _IFADDRS_H_
|
||||
|
||||
struct ifaddrs {
|
||||
struct ifaddrs *ifa_next;
|
||||
char *ifa_name;
|
||||
unsigned int ifa_flags;
|
||||
struct sockaddr *ifa_addr;
|
||||
struct sockaddr *ifa_netmask;
|
||||
struct sockaddr *ifa_dstaddr;
|
||||
void *ifa_data;
|
||||
};
|
||||
|
||||
/*
|
||||
* This may have been defined in <net/if.h>. Note that if <net/if.h> is
|
||||
* to be included it must be included before this header file.
|
||||
*/
|
||||
#ifndef ifa_broadaddr
|
||||
#define ifa_broadaddr ifa_dstaddr /* broadcast address interface */
|
||||
#endif
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
|
||||
__BEGIN_DECLS
|
||||
extern int getifaddrs(struct ifaddrs **ifap);
|
||||
extern void freeifaddrs(struct ifaddrs *ifa);
|
||||
__END_DECLS
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,256 @@
|
||||
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "uv.h"
|
||||
#include "uv-common.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
struct poll_ctx {
|
||||
uv_fs_poll_t* parent_handle; /* NULL if parent has been stopped or closed */
|
||||
int busy_polling;
|
||||
unsigned int interval;
|
||||
uint64_t start_time;
|
||||
uv_loop_t* loop;
|
||||
uv_fs_poll_cb poll_cb;
|
||||
uv_timer_t timer_handle;
|
||||
uv_fs_t fs_req; /* TODO(bnoordhuis) mark fs_req internal */
|
||||
uv_stat_t statbuf;
|
||||
char path[1]; /* variable length */
|
||||
};
|
||||
|
||||
static int statbuf_eq(const uv_stat_t* a, const uv_stat_t* b);
|
||||
static void poll_cb(uv_fs_t* req);
|
||||
static void timer_cb(uv_timer_t* timer);
|
||||
static void timer_close_cb(uv_handle_t* handle);
|
||||
|
||||
static uv_stat_t zero_statbuf;
|
||||
|
||||
|
||||
int uv_fs_poll_init(uv_loop_t* loop, uv_fs_poll_t* handle) {
|
||||
uv__handle_init(loop, (uv_handle_t*)handle, UV_FS_POLL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv_fs_poll_start(uv_fs_poll_t* handle,
|
||||
uv_fs_poll_cb cb,
|
||||
const char* path,
|
||||
unsigned int interval) {
|
||||
struct poll_ctx* ctx;
|
||||
uv_loop_t* loop;
|
||||
size_t len;
|
||||
int err;
|
||||
|
||||
if (uv__is_active(handle))
|
||||
return 0;
|
||||
|
||||
loop = handle->loop;
|
||||
len = strlen(path);
|
||||
ctx = uv__calloc(1, sizeof(*ctx) + len);
|
||||
|
||||
if (ctx == NULL)
|
||||
return UV_ENOMEM;
|
||||
|
||||
ctx->loop = loop;
|
||||
ctx->poll_cb = cb;
|
||||
ctx->interval = interval ? interval : 1;
|
||||
ctx->start_time = uv_now(loop);
|
||||
ctx->parent_handle = handle;
|
||||
memcpy(ctx->path, path, len + 1);
|
||||
|
||||
err = uv_timer_init(loop, &ctx->timer_handle);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
ctx->timer_handle.flags |= UV__HANDLE_INTERNAL;
|
||||
uv__handle_unref(&ctx->timer_handle);
|
||||
|
||||
err = uv_fs_stat(loop, &ctx->fs_req, ctx->path, poll_cb);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
handle->poll_ctx = ctx;
|
||||
uv__handle_start(handle);
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
uv__free(ctx);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
int uv_fs_poll_stop(uv_fs_poll_t* handle) {
|
||||
struct poll_ctx* ctx;
|
||||
|
||||
if (!uv__is_active(handle))
|
||||
return 0;
|
||||
|
||||
ctx = handle->poll_ctx;
|
||||
assert(ctx != NULL);
|
||||
assert(ctx->parent_handle != NULL);
|
||||
ctx->parent_handle = NULL;
|
||||
handle->poll_ctx = NULL;
|
||||
|
||||
/* Close the timer if it's active. If it's inactive, there's a stat request
|
||||
* in progress and poll_cb will take care of the cleanup.
|
||||
*/
|
||||
if (uv__is_active(&ctx->timer_handle))
|
||||
uv_close((uv_handle_t*)&ctx->timer_handle, timer_close_cb);
|
||||
|
||||
uv__handle_stop(handle);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv_fs_poll_getpath(uv_fs_poll_t* handle, char* buffer, size_t* size) {
|
||||
struct poll_ctx* ctx;
|
||||
size_t required_len;
|
||||
|
||||
if (!uv__is_active(handle)) {
|
||||
*size = 0;
|
||||
return UV_EINVAL;
|
||||
}
|
||||
|
||||
ctx = handle->poll_ctx;
|
||||
assert(ctx != NULL);
|
||||
|
||||
required_len = strlen(ctx->path);
|
||||
if (required_len >= *size) {
|
||||
*size = required_len + 1;
|
||||
return UV_ENOBUFS;
|
||||
}
|
||||
|
||||
memcpy(buffer, ctx->path, required_len);
|
||||
*size = required_len;
|
||||
buffer[required_len] = '\0';
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void uv__fs_poll_close(uv_fs_poll_t* handle) {
|
||||
uv_fs_poll_stop(handle);
|
||||
}
|
||||
|
||||
|
||||
static void timer_cb(uv_timer_t* timer) {
|
||||
struct poll_ctx* ctx;
|
||||
|
||||
ctx = container_of(timer, struct poll_ctx, timer_handle);
|
||||
assert(ctx->parent_handle != NULL);
|
||||
assert(ctx->parent_handle->poll_ctx == ctx);
|
||||
ctx->start_time = uv_now(ctx->loop);
|
||||
|
||||
if (uv_fs_stat(ctx->loop, &ctx->fs_req, ctx->path, poll_cb))
|
||||
abort();
|
||||
}
|
||||
|
||||
|
||||
static void poll_cb(uv_fs_t* req) {
|
||||
uv_stat_t* statbuf;
|
||||
struct poll_ctx* ctx;
|
||||
uint64_t interval;
|
||||
|
||||
ctx = container_of(req, struct poll_ctx, fs_req);
|
||||
|
||||
if (ctx->parent_handle == NULL) { /* handle has been stopped or closed */
|
||||
uv_close((uv_handle_t*)&ctx->timer_handle, timer_close_cb);
|
||||
uv_fs_req_cleanup(req);
|
||||
return;
|
||||
}
|
||||
|
||||
if (req->result != 0) {
|
||||
if (ctx->busy_polling != req->result) {
|
||||
ctx->poll_cb(ctx->parent_handle,
|
||||
req->result,
|
||||
&ctx->statbuf,
|
||||
&zero_statbuf);
|
||||
ctx->busy_polling = req->result;
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
|
||||
statbuf = &req->statbuf;
|
||||
|
||||
if (ctx->busy_polling != 0)
|
||||
if (ctx->busy_polling < 0 || !statbuf_eq(&ctx->statbuf, statbuf))
|
||||
ctx->poll_cb(ctx->parent_handle, 0, &ctx->statbuf, statbuf);
|
||||
|
||||
ctx->statbuf = *statbuf;
|
||||
ctx->busy_polling = 1;
|
||||
|
||||
out:
|
||||
uv_fs_req_cleanup(req);
|
||||
|
||||
if (ctx->parent_handle == NULL) { /* handle has been stopped by callback */
|
||||
uv_close((uv_handle_t*)&ctx->timer_handle, timer_close_cb);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Reschedule timer, subtract the delay from doing the stat(). */
|
||||
interval = ctx->interval;
|
||||
interval -= (uv_now(ctx->loop) - ctx->start_time) % interval;
|
||||
|
||||
if (uv_timer_start(&ctx->timer_handle, timer_cb, interval, 0))
|
||||
abort();
|
||||
}
|
||||
|
||||
|
||||
static void timer_close_cb(uv_handle_t* handle) {
|
||||
uv__free(container_of(handle, struct poll_ctx, timer_handle));
|
||||
}
|
||||
|
||||
|
||||
static int statbuf_eq(const uv_stat_t* a, const uv_stat_t* b) {
|
||||
return a->st_ctim.tv_nsec == b->st_ctim.tv_nsec
|
||||
&& a->st_mtim.tv_nsec == b->st_mtim.tv_nsec
|
||||
&& a->st_birthtim.tv_nsec == b->st_birthtim.tv_nsec
|
||||
&& a->st_ctim.tv_sec == b->st_ctim.tv_sec
|
||||
&& a->st_mtim.tv_sec == b->st_mtim.tv_sec
|
||||
&& a->st_birthtim.tv_sec == b->st_birthtim.tv_sec
|
||||
&& a->st_size == b->st_size
|
||||
&& a->st_mode == b->st_mode
|
||||
&& a->st_uid == b->st_uid
|
||||
&& a->st_gid == b->st_gid
|
||||
&& a->st_ino == b->st_ino
|
||||
&& a->st_dev == b->st_dev
|
||||
&& a->st_flags == b->st_flags
|
||||
&& a->st_gen == b->st_gen;
|
||||
}
|
||||
|
||||
|
||||
#if defined(_WIN32)
|
||||
|
||||
#include "win/internal.h"
|
||||
#include "win/handle-inl.h"
|
||||
|
||||
void uv__fs_poll_endgame(uv_loop_t* loop, uv_fs_poll_t* handle) {
|
||||
assert(handle->flags & UV__HANDLE_CLOSING);
|
||||
assert(!(handle->flags & UV_HANDLE_CLOSED));
|
||||
uv__handle_close(handle);
|
||||
}
|
||||
|
||||
#endif /* _WIN32 */
|
||||
@@ -0,0 +1,245 @@
|
||||
/* Copyright (c) 2013, Ben Noordhuis <info@bnoordhuis.nl>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef UV_SRC_HEAP_H_
|
||||
#define UV_SRC_HEAP_H_
|
||||
|
||||
#include <stddef.h> /* NULL */
|
||||
|
||||
#if defined(__GNUC__)
|
||||
# define HEAP_EXPORT(declaration) __attribute__((unused)) static declaration
|
||||
#else
|
||||
# define HEAP_EXPORT(declaration) static declaration
|
||||
#endif
|
||||
|
||||
struct heap_node {
|
||||
struct heap_node* left;
|
||||
struct heap_node* right;
|
||||
struct heap_node* parent;
|
||||
};
|
||||
|
||||
/* A binary min heap. The usual properties hold: the root is the lowest
|
||||
* element in the set, the height of the tree is at most log2(nodes) and
|
||||
* it's always a complete binary tree.
|
||||
*
|
||||
* The heap function try hard to detect corrupted tree nodes at the cost
|
||||
* of a minor reduction in performance. Compile with -DNDEBUG to disable.
|
||||
*/
|
||||
struct heap {
|
||||
struct heap_node* min;
|
||||
unsigned int nelts;
|
||||
};
|
||||
|
||||
/* Return non-zero if a < b. */
|
||||
typedef int (*heap_compare_fn)(const struct heap_node* a,
|
||||
const struct heap_node* b);
|
||||
|
||||
/* Public functions. */
|
||||
HEAP_EXPORT(void heap_init(struct heap* heap));
|
||||
HEAP_EXPORT(struct heap_node* heap_min(const struct heap* heap));
|
||||
HEAP_EXPORT(void heap_insert(struct heap* heap,
|
||||
struct heap_node* newnode,
|
||||
heap_compare_fn less_than));
|
||||
HEAP_EXPORT(void heap_remove(struct heap* heap,
|
||||
struct heap_node* node,
|
||||
heap_compare_fn less_than));
|
||||
HEAP_EXPORT(void heap_dequeue(struct heap* heap, heap_compare_fn less_than));
|
||||
|
||||
/* Implementation follows. */
|
||||
|
||||
HEAP_EXPORT(void heap_init(struct heap* heap)) {
|
||||
heap->min = NULL;
|
||||
heap->nelts = 0;
|
||||
}
|
||||
|
||||
HEAP_EXPORT(struct heap_node* heap_min(const struct heap* heap)) {
|
||||
return heap->min;
|
||||
}
|
||||
|
||||
/* Swap parent with child. Child moves closer to the root, parent moves away. */
|
||||
static void heap_node_swap(struct heap* heap,
|
||||
struct heap_node* parent,
|
||||
struct heap_node* child) {
|
||||
struct heap_node* sibling;
|
||||
struct heap_node t;
|
||||
|
||||
t = *parent;
|
||||
*parent = *child;
|
||||
*child = t;
|
||||
|
||||
parent->parent = child;
|
||||
if (child->left == child) {
|
||||
child->left = parent;
|
||||
sibling = child->right;
|
||||
} else {
|
||||
child->right = parent;
|
||||
sibling = child->left;
|
||||
}
|
||||
if (sibling != NULL)
|
||||
sibling->parent = child;
|
||||
|
||||
if (parent->left != NULL)
|
||||
parent->left->parent = parent;
|
||||
if (parent->right != NULL)
|
||||
parent->right->parent = parent;
|
||||
|
||||
if (child->parent == NULL)
|
||||
heap->min = child;
|
||||
else if (child->parent->left == parent)
|
||||
child->parent->left = child;
|
||||
else
|
||||
child->parent->right = child;
|
||||
}
|
||||
|
||||
HEAP_EXPORT(void heap_insert(struct heap* heap,
|
||||
struct heap_node* newnode,
|
||||
heap_compare_fn less_than)) {
|
||||
struct heap_node** parent;
|
||||
struct heap_node** child;
|
||||
unsigned int path;
|
||||
unsigned int n;
|
||||
unsigned int k;
|
||||
|
||||
newnode->left = NULL;
|
||||
newnode->right = NULL;
|
||||
newnode->parent = NULL;
|
||||
|
||||
/* Calculate the path from the root to the insertion point. This is a min
|
||||
* heap so we always insert at the left-most free node of the bottom row.
|
||||
*/
|
||||
path = 0;
|
||||
for (k = 0, n = 1 + heap->nelts; n >= 2; k += 1, n /= 2)
|
||||
path = (path << 1) | (n & 1);
|
||||
|
||||
/* Now traverse the heap using the path we calculated in the previous step. */
|
||||
parent = child = &heap->min;
|
||||
while (k > 0) {
|
||||
parent = child;
|
||||
if (path & 1)
|
||||
child = &(*child)->right;
|
||||
else
|
||||
child = &(*child)->left;
|
||||
path >>= 1;
|
||||
k -= 1;
|
||||
}
|
||||
|
||||
/* Insert the new node. */
|
||||
newnode->parent = *parent;
|
||||
*child = newnode;
|
||||
heap->nelts += 1;
|
||||
|
||||
/* Walk up the tree and check at each node if the heap property holds.
|
||||
* It's a min heap so parent < child must be true.
|
||||
*/
|
||||
while (newnode->parent != NULL && less_than(newnode, newnode->parent))
|
||||
heap_node_swap(heap, newnode->parent, newnode);
|
||||
}
|
||||
|
||||
HEAP_EXPORT(void heap_remove(struct heap* heap,
|
||||
struct heap_node* node,
|
||||
heap_compare_fn less_than)) {
|
||||
struct heap_node* smallest;
|
||||
struct heap_node** max;
|
||||
struct heap_node* child;
|
||||
unsigned int path;
|
||||
unsigned int k;
|
||||
unsigned int n;
|
||||
|
||||
if (heap->nelts == 0)
|
||||
return;
|
||||
|
||||
/* Calculate the path from the min (the root) to the max, the left-most node
|
||||
* of the bottom row.
|
||||
*/
|
||||
path = 0;
|
||||
for (k = 0, n = heap->nelts; n >= 2; k += 1, n /= 2)
|
||||
path = (path << 1) | (n & 1);
|
||||
|
||||
/* Now traverse the heap using the path we calculated in the previous step. */
|
||||
max = &heap->min;
|
||||
while (k > 0) {
|
||||
if (path & 1)
|
||||
max = &(*max)->right;
|
||||
else
|
||||
max = &(*max)->left;
|
||||
path >>= 1;
|
||||
k -= 1;
|
||||
}
|
||||
|
||||
heap->nelts -= 1;
|
||||
|
||||
/* Unlink the max node. */
|
||||
child = *max;
|
||||
*max = NULL;
|
||||
|
||||
if (child == node) {
|
||||
/* We're removing either the max or the last node in the tree. */
|
||||
if (child == heap->min) {
|
||||
heap->min = NULL;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* Replace the to be deleted node with the max node. */
|
||||
child->left = node->left;
|
||||
child->right = node->right;
|
||||
child->parent = node->parent;
|
||||
|
||||
if (child->left != NULL) {
|
||||
child->left->parent = child;
|
||||
}
|
||||
|
||||
if (child->right != NULL) {
|
||||
child->right->parent = child;
|
||||
}
|
||||
|
||||
if (node->parent == NULL) {
|
||||
heap->min = child;
|
||||
} else if (node->parent->left == node) {
|
||||
node->parent->left = child;
|
||||
} else {
|
||||
node->parent->right = child;
|
||||
}
|
||||
|
||||
/* Walk down the subtree and check at each node if the heap property holds.
|
||||
* It's a min heap so parent < child must be true. If the parent is bigger,
|
||||
* swap it with the smallest child.
|
||||
*/
|
||||
for (;;) {
|
||||
smallest = child;
|
||||
if (child->left != NULL && less_than(child->left, smallest))
|
||||
smallest = child->left;
|
||||
if (child->right != NULL && less_than(child->right, smallest))
|
||||
smallest = child->right;
|
||||
if (smallest == child)
|
||||
break;
|
||||
heap_node_swap(heap, child, smallest);
|
||||
}
|
||||
|
||||
/* Walk up the subtree and check that each parent is less than the node
|
||||
* this is required, because `max` node is not guaranteed to be the
|
||||
* actual maximum in tree
|
||||
*/
|
||||
while (child->parent != NULL && less_than(child, child->parent))
|
||||
heap_node_swap(heap, child->parent, child);
|
||||
}
|
||||
|
||||
HEAP_EXPORT(void heap_dequeue(struct heap* heap, heap_compare_fn less_than)) {
|
||||
heap_remove(heap, heap->min, less_than);
|
||||
}
|
||||
|
||||
#undef HEAP_EXPORT
|
||||
|
||||
#endif /* UV_SRC_HEAP_H_ */
|
||||
@@ -0,0 +1,309 @@
|
||||
/*
|
||||
* Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
|
||||
* Copyright (c) 1996-1999 by Internet Software Consortium.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
|
||||
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#if defined(_MSC_VER) && _MSC_VER < 1600
|
||||
# include "stdint-msvc2008.h"
|
||||
#else
|
||||
# include <stdint.h>
|
||||
#endif
|
||||
|
||||
#include "uv.h"
|
||||
#include "uv-common.h"
|
||||
|
||||
#define UV__INET_ADDRSTRLEN 16
|
||||
#define UV__INET6_ADDRSTRLEN 46
|
||||
|
||||
|
||||
static int inet_ntop4(const unsigned char *src, char *dst, size_t size);
|
||||
static int inet_ntop6(const unsigned char *src, char *dst, size_t size);
|
||||
static int inet_pton4(const char *src, unsigned char *dst);
|
||||
static int inet_pton6(const char *src, unsigned char *dst);
|
||||
|
||||
|
||||
int uv_inet_ntop(int af, const void* src, char* dst, size_t size) {
|
||||
switch (af) {
|
||||
case AF_INET:
|
||||
return (inet_ntop4(src, dst, size));
|
||||
case AF_INET6:
|
||||
return (inet_ntop6(src, dst, size));
|
||||
default:
|
||||
return UV_EAFNOSUPPORT;
|
||||
}
|
||||
/* NOTREACHED */
|
||||
}
|
||||
|
||||
|
||||
static int inet_ntop4(const unsigned char *src, char *dst, size_t size) {
|
||||
static const char fmt[] = "%u.%u.%u.%u";
|
||||
char tmp[UV__INET_ADDRSTRLEN];
|
||||
int l;
|
||||
|
||||
l = snprintf(tmp, sizeof(tmp), fmt, src[0], src[1], src[2], src[3]);
|
||||
if (l <= 0 || (size_t) l >= size) {
|
||||
return UV_ENOSPC;
|
||||
}
|
||||
strncpy(dst, tmp, size);
|
||||
dst[size - 1] = '\0';
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int inet_ntop6(const unsigned char *src, char *dst, size_t size) {
|
||||
/*
|
||||
* Note that int32_t and int16_t need only be "at least" large enough
|
||||
* to contain a value of the specified size. On some systems, like
|
||||
* Crays, there is no such thing as an integer variable with 16 bits.
|
||||
* Keep this in mind if you think this function should have been coded
|
||||
* to use pointer overlays. All the world's not a VAX.
|
||||
*/
|
||||
char tmp[UV__INET6_ADDRSTRLEN], *tp;
|
||||
struct { int base, len; } best, cur;
|
||||
unsigned int words[sizeof(struct in6_addr) / sizeof(uint16_t)];
|
||||
int i;
|
||||
|
||||
/*
|
||||
* Preprocess:
|
||||
* Copy the input (bytewise) array into a wordwise array.
|
||||
* Find the longest run of 0x00's in src[] for :: shorthanding.
|
||||
*/
|
||||
memset(words, '\0', sizeof words);
|
||||
for (i = 0; i < (int) sizeof(struct in6_addr); i++)
|
||||
words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3));
|
||||
best.base = -1;
|
||||
best.len = 0;
|
||||
cur.base = -1;
|
||||
cur.len = 0;
|
||||
for (i = 0; i < (int) ARRAY_SIZE(words); i++) {
|
||||
if (words[i] == 0) {
|
||||
if (cur.base == -1)
|
||||
cur.base = i, cur.len = 1;
|
||||
else
|
||||
cur.len++;
|
||||
} else {
|
||||
if (cur.base != -1) {
|
||||
if (best.base == -1 || cur.len > best.len)
|
||||
best = cur;
|
||||
cur.base = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (cur.base != -1) {
|
||||
if (best.base == -1 || cur.len > best.len)
|
||||
best = cur;
|
||||
}
|
||||
if (best.base != -1 && best.len < 2)
|
||||
best.base = -1;
|
||||
|
||||
/*
|
||||
* Format the result.
|
||||
*/
|
||||
tp = tmp;
|
||||
for (i = 0; i < (int) ARRAY_SIZE(words); i++) {
|
||||
/* Are we inside the best run of 0x00's? */
|
||||
if (best.base != -1 && i >= best.base &&
|
||||
i < (best.base + best.len)) {
|
||||
if (i == best.base)
|
||||
*tp++ = ':';
|
||||
continue;
|
||||
}
|
||||
/* Are we following an initial run of 0x00s or any real hex? */
|
||||
if (i != 0)
|
||||
*tp++ = ':';
|
||||
/* Is this address an encapsulated IPv4? */
|
||||
if (i == 6 && best.base == 0 && (best.len == 6 ||
|
||||
(best.len == 7 && words[7] != 0x0001) ||
|
||||
(best.len == 5 && words[5] == 0xffff))) {
|
||||
int err = inet_ntop4(src+12, tp, sizeof tmp - (tp - tmp));
|
||||
if (err)
|
||||
return err;
|
||||
tp += strlen(tp);
|
||||
break;
|
||||
}
|
||||
tp += sprintf(tp, "%x", words[i]);
|
||||
}
|
||||
/* Was it a trailing run of 0x00's? */
|
||||
if (best.base != -1 && (best.base + best.len) == ARRAY_SIZE(words))
|
||||
*tp++ = ':';
|
||||
*tp++ = '\0';
|
||||
|
||||
/*
|
||||
* Check for overflow, copy, and we're done.
|
||||
*/
|
||||
if ((size_t)(tp - tmp) > size) {
|
||||
return UV_ENOSPC;
|
||||
}
|
||||
strcpy(dst, tmp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv_inet_pton(int af, const char* src, void* dst) {
|
||||
if (src == NULL || dst == NULL)
|
||||
return UV_EINVAL;
|
||||
|
||||
switch (af) {
|
||||
case AF_INET:
|
||||
return (inet_pton4(src, dst));
|
||||
case AF_INET6: {
|
||||
int len;
|
||||
char tmp[UV__INET6_ADDRSTRLEN], *s, *p;
|
||||
s = (char*) src;
|
||||
p = strchr(src, '%');
|
||||
if (p != NULL) {
|
||||
s = tmp;
|
||||
len = p - src;
|
||||
if (len > UV__INET6_ADDRSTRLEN-1)
|
||||
return UV_EINVAL;
|
||||
memcpy(s, src, len);
|
||||
s[len] = '\0';
|
||||
}
|
||||
return inet_pton6(s, dst);
|
||||
}
|
||||
default:
|
||||
return UV_EAFNOSUPPORT;
|
||||
}
|
||||
/* NOTREACHED */
|
||||
}
|
||||
|
||||
|
||||
static int inet_pton4(const char *src, unsigned char *dst) {
|
||||
static const char digits[] = "0123456789";
|
||||
int saw_digit, octets, ch;
|
||||
unsigned char tmp[sizeof(struct in_addr)], *tp;
|
||||
|
||||
saw_digit = 0;
|
||||
octets = 0;
|
||||
*(tp = tmp) = 0;
|
||||
while ((ch = *src++) != '\0') {
|
||||
const char *pch;
|
||||
|
||||
if ((pch = strchr(digits, ch)) != NULL) {
|
||||
unsigned int nw = *tp * 10 + (pch - digits);
|
||||
|
||||
if (saw_digit && *tp == 0)
|
||||
return UV_EINVAL;
|
||||
if (nw > 255)
|
||||
return UV_EINVAL;
|
||||
*tp = nw;
|
||||
if (!saw_digit) {
|
||||
if (++octets > 4)
|
||||
return UV_EINVAL;
|
||||
saw_digit = 1;
|
||||
}
|
||||
} else if (ch == '.' && saw_digit) {
|
||||
if (octets == 4)
|
||||
return UV_EINVAL;
|
||||
*++tp = 0;
|
||||
saw_digit = 0;
|
||||
} else
|
||||
return UV_EINVAL;
|
||||
}
|
||||
if (octets < 4)
|
||||
return UV_EINVAL;
|
||||
memcpy(dst, tmp, sizeof(struct in_addr));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int inet_pton6(const char *src, unsigned char *dst) {
|
||||
static const char xdigits_l[] = "0123456789abcdef",
|
||||
xdigits_u[] = "0123456789ABCDEF";
|
||||
unsigned char tmp[sizeof(struct in6_addr)], *tp, *endp, *colonp;
|
||||
const char *xdigits, *curtok;
|
||||
int ch, seen_xdigits;
|
||||
unsigned int val;
|
||||
|
||||
memset((tp = tmp), '\0', sizeof tmp);
|
||||
endp = tp + sizeof tmp;
|
||||
colonp = NULL;
|
||||
/* Leading :: requires some special handling. */
|
||||
if (*src == ':')
|
||||
if (*++src != ':')
|
||||
return UV_EINVAL;
|
||||
curtok = src;
|
||||
seen_xdigits = 0;
|
||||
val = 0;
|
||||
while ((ch = *src++) != '\0') {
|
||||
const char *pch;
|
||||
|
||||
if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL)
|
||||
pch = strchr((xdigits = xdigits_u), ch);
|
||||
if (pch != NULL) {
|
||||
val <<= 4;
|
||||
val |= (pch - xdigits);
|
||||
if (++seen_xdigits > 4)
|
||||
return UV_EINVAL;
|
||||
continue;
|
||||
}
|
||||
if (ch == ':') {
|
||||
curtok = src;
|
||||
if (!seen_xdigits) {
|
||||
if (colonp)
|
||||
return UV_EINVAL;
|
||||
colonp = tp;
|
||||
continue;
|
||||
} else if (*src == '\0') {
|
||||
return UV_EINVAL;
|
||||
}
|
||||
if (tp + sizeof(uint16_t) > endp)
|
||||
return UV_EINVAL;
|
||||
*tp++ = (unsigned char) (val >> 8) & 0xff;
|
||||
*tp++ = (unsigned char) val & 0xff;
|
||||
seen_xdigits = 0;
|
||||
val = 0;
|
||||
continue;
|
||||
}
|
||||
if (ch == '.' && ((tp + sizeof(struct in_addr)) <= endp)) {
|
||||
int err = inet_pton4(curtok, tp);
|
||||
if (err == 0) {
|
||||
tp += sizeof(struct in_addr);
|
||||
seen_xdigits = 0;
|
||||
break; /*%< '\\0' was seen by inet_pton4(). */
|
||||
}
|
||||
}
|
||||
return UV_EINVAL;
|
||||
}
|
||||
if (seen_xdigits) {
|
||||
if (tp + sizeof(uint16_t) > endp)
|
||||
return UV_EINVAL;
|
||||
*tp++ = (unsigned char) (val >> 8) & 0xff;
|
||||
*tp++ = (unsigned char) val & 0xff;
|
||||
}
|
||||
if (colonp != NULL) {
|
||||
/*
|
||||
* Since some memmove()'s erroneously fail to handle
|
||||
* overlapping regions, we'll do the shift by hand.
|
||||
*/
|
||||
const int n = tp - colonp;
|
||||
int i;
|
||||
|
||||
if (tp == endp)
|
||||
return UV_EINVAL;
|
||||
for (i = 1; i <= n; i++) {
|
||||
endp[- i] = colonp[n - i];
|
||||
colonp[n - i] = 0;
|
||||
}
|
||||
tp = endp;
|
||||
}
|
||||
if (tp != endp)
|
||||
return UV_EINVAL;
|
||||
memcpy(dst, tmp, sizeof tmp);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
Copyright (c) 2016, Kari Tristan Helgason <kthelgason@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _UV_PTHREAD_BARRIER_
|
||||
#define _UV_PTHREAD_BARRIER_
|
||||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
#if !defined(__MVS__)
|
||||
#include <semaphore.h> /* sem_t */
|
||||
#endif
|
||||
|
||||
#define PTHREAD_BARRIER_SERIAL_THREAD 0x12345
|
||||
|
||||
/*
|
||||
* To maintain ABI compatibility with
|
||||
* libuv v1.x struct is padded according
|
||||
* to target platform
|
||||
*/
|
||||
#if defined(__ANDROID__)
|
||||
# define UV_BARRIER_STRUCT_PADDING \
|
||||
sizeof(pthread_mutex_t) + \
|
||||
sizeof(pthread_cond_t) + \
|
||||
sizeof(unsigned int) - \
|
||||
sizeof(void *)
|
||||
#elif defined(__APPLE__)
|
||||
# define UV_BARRIER_STRUCT_PADDING \
|
||||
sizeof(pthread_mutex_t) + \
|
||||
2 * sizeof(sem_t) + \
|
||||
2 * sizeof(unsigned int) - \
|
||||
sizeof(void *)
|
||||
#else
|
||||
# define UV_BARRIER_STRUCT_PADDING 0
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
pthread_mutex_t mutex;
|
||||
pthread_cond_t cond;
|
||||
unsigned threshold;
|
||||
unsigned in;
|
||||
unsigned out;
|
||||
} _uv_barrier;
|
||||
|
||||
typedef struct {
|
||||
_uv_barrier* b;
|
||||
char _pad[UV_BARRIER_STRUCT_PADDING];
|
||||
} pthread_barrier_t;
|
||||
|
||||
int pthread_barrier_init(pthread_barrier_t* barrier,
|
||||
const void* barrier_attr,
|
||||
unsigned count);
|
||||
|
||||
int pthread_barrier_wait(pthread_barrier_t* barrier);
|
||||
int pthread_barrier_destroy(pthread_barrier_t *barrier);
|
||||
|
||||
#endif /* _UV_PTHREAD_BARRIER_ */
|
||||
@@ -0,0 +1,108 @@
|
||||
/* Copyright (c) 2013, Ben Noordhuis <info@bnoordhuis.nl>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef QUEUE_H_
|
||||
#define QUEUE_H_
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
typedef void *QUEUE[2];
|
||||
|
||||
/* Private macros. */
|
||||
#define QUEUE_NEXT(q) (*(QUEUE **) &((*(q))[0]))
|
||||
#define QUEUE_PREV(q) (*(QUEUE **) &((*(q))[1]))
|
||||
#define QUEUE_PREV_NEXT(q) (QUEUE_NEXT(QUEUE_PREV(q)))
|
||||
#define QUEUE_NEXT_PREV(q) (QUEUE_PREV(QUEUE_NEXT(q)))
|
||||
|
||||
/* Public macros. */
|
||||
#define QUEUE_DATA(ptr, type, field) \
|
||||
((type *) ((char *) (ptr) - offsetof(type, field)))
|
||||
|
||||
/* Important note: mutating the list while QUEUE_FOREACH is
|
||||
* iterating over its elements results in undefined behavior.
|
||||
*/
|
||||
#define QUEUE_FOREACH(q, h) \
|
||||
for ((q) = QUEUE_NEXT(h); (q) != (h); (q) = QUEUE_NEXT(q))
|
||||
|
||||
#define QUEUE_EMPTY(q) \
|
||||
((const QUEUE *) (q) == (const QUEUE *) QUEUE_NEXT(q))
|
||||
|
||||
#define QUEUE_HEAD(q) \
|
||||
(QUEUE_NEXT(q))
|
||||
|
||||
#define QUEUE_INIT(q) \
|
||||
do { \
|
||||
QUEUE_NEXT(q) = (q); \
|
||||
QUEUE_PREV(q) = (q); \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
#define QUEUE_ADD(h, n) \
|
||||
do { \
|
||||
QUEUE_PREV_NEXT(h) = QUEUE_NEXT(n); \
|
||||
QUEUE_NEXT_PREV(n) = QUEUE_PREV(h); \
|
||||
QUEUE_PREV(h) = QUEUE_PREV(n); \
|
||||
QUEUE_PREV_NEXT(h) = (h); \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
#define QUEUE_SPLIT(h, q, n) \
|
||||
do { \
|
||||
QUEUE_PREV(n) = QUEUE_PREV(h); \
|
||||
QUEUE_PREV_NEXT(n) = (n); \
|
||||
QUEUE_NEXT(n) = (q); \
|
||||
QUEUE_PREV(h) = QUEUE_PREV(q); \
|
||||
QUEUE_PREV_NEXT(h) = (h); \
|
||||
QUEUE_PREV(q) = (n); \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
#define QUEUE_MOVE(h, n) \
|
||||
do { \
|
||||
if (QUEUE_EMPTY(h)) \
|
||||
QUEUE_INIT(n); \
|
||||
else { \
|
||||
QUEUE* q = QUEUE_HEAD(h); \
|
||||
QUEUE_SPLIT(h, q, n); \
|
||||
} \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
#define QUEUE_INSERT_HEAD(h, q) \
|
||||
do { \
|
||||
QUEUE_NEXT(q) = QUEUE_NEXT(h); \
|
||||
QUEUE_PREV(q) = (h); \
|
||||
QUEUE_NEXT_PREV(q) = (q); \
|
||||
QUEUE_NEXT(h) = (q); \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
#define QUEUE_INSERT_TAIL(h, q) \
|
||||
do { \
|
||||
QUEUE_NEXT(q) = (h); \
|
||||
QUEUE_PREV(q) = QUEUE_PREV(h); \
|
||||
QUEUE_PREV_NEXT(q) = (q); \
|
||||
QUEUE_PREV(h) = (q); \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
#define QUEUE_REMOVE(q) \
|
||||
do { \
|
||||
QUEUE_PREV_NEXT(q) = QUEUE_NEXT(q); \
|
||||
QUEUE_NEXT_PREV(q) = QUEUE_PREV(q); \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
#endif /* QUEUE_H_ */
|
||||
@@ -0,0 +1,247 @@
|
||||
// ISO C9x compliant stdint.h for Microsoft Visual Studio
|
||||
// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124
|
||||
//
|
||||
// Copyright (c) 2006-2008 Alexander Chemeris
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. 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.
|
||||
//
|
||||
// 3. The name of the author may be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef _MSC_VER // [
|
||||
#error "Use this header only with Microsoft Visual C++ compilers!"
|
||||
#endif // _MSC_VER ]
|
||||
|
||||
#ifndef _MSC_STDINT_H_ // [
|
||||
#define _MSC_STDINT_H_
|
||||
|
||||
#if _MSC_VER > 1000
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
// For Visual Studio 6 in C++ mode and for many Visual Studio versions when
|
||||
// compiling for ARM we should wrap <wchar.h> include with 'extern "C++" {}'
|
||||
// or compiler give many errors like this:
|
||||
// error C2733: second C linkage of overloaded function 'wmemchr' not allowed
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
# include <wchar.h>
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
// Define _W64 macros to mark types changing their size, like intptr_t.
|
||||
#ifndef _W64
|
||||
# if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300
|
||||
# define _W64 __w64
|
||||
# else
|
||||
# define _W64
|
||||
# endif
|
||||
#endif
|
||||
|
||||
|
||||
// 7.18.1 Integer types
|
||||
|
||||
// 7.18.1.1 Exact-width integer types
|
||||
|
||||
// Visual Studio 6 and Embedded Visual C++ 4 doesn't
|
||||
// realize that, e.g. char has the same size as __int8
|
||||
// so we give up on __intX for them.
|
||||
#if (_MSC_VER < 1300)
|
||||
typedef signed char int8_t;
|
||||
typedef signed short int16_t;
|
||||
typedef signed int int32_t;
|
||||
typedef unsigned char uint8_t;
|
||||
typedef unsigned short uint16_t;
|
||||
typedef unsigned int uint32_t;
|
||||
#else
|
||||
typedef signed __int8 int8_t;
|
||||
typedef signed __int16 int16_t;
|
||||
typedef signed __int32 int32_t;
|
||||
typedef unsigned __int8 uint8_t;
|
||||
typedef unsigned __int16 uint16_t;
|
||||
typedef unsigned __int32 uint32_t;
|
||||
#endif
|
||||
typedef signed __int64 int64_t;
|
||||
typedef unsigned __int64 uint64_t;
|
||||
|
||||
|
||||
// 7.18.1.2 Minimum-width integer types
|
||||
typedef int8_t int_least8_t;
|
||||
typedef int16_t int_least16_t;
|
||||
typedef int32_t int_least32_t;
|
||||
typedef int64_t int_least64_t;
|
||||
typedef uint8_t uint_least8_t;
|
||||
typedef uint16_t uint_least16_t;
|
||||
typedef uint32_t uint_least32_t;
|
||||
typedef uint64_t uint_least64_t;
|
||||
|
||||
// 7.18.1.3 Fastest minimum-width integer types
|
||||
typedef int8_t int_fast8_t;
|
||||
typedef int16_t int_fast16_t;
|
||||
typedef int32_t int_fast32_t;
|
||||
typedef int64_t int_fast64_t;
|
||||
typedef uint8_t uint_fast8_t;
|
||||
typedef uint16_t uint_fast16_t;
|
||||
typedef uint32_t uint_fast32_t;
|
||||
typedef uint64_t uint_fast64_t;
|
||||
|
||||
// 7.18.1.4 Integer types capable of holding object pointers
|
||||
#ifdef _WIN64 // [
|
||||
typedef signed __int64 intptr_t;
|
||||
typedef unsigned __int64 uintptr_t;
|
||||
#else // _WIN64 ][
|
||||
typedef _W64 signed int intptr_t;
|
||||
typedef _W64 unsigned int uintptr_t;
|
||||
#endif // _WIN64 ]
|
||||
|
||||
// 7.18.1.5 Greatest-width integer types
|
||||
typedef int64_t intmax_t;
|
||||
typedef uint64_t uintmax_t;
|
||||
|
||||
|
||||
// 7.18.2 Limits of specified-width integer types
|
||||
|
||||
#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [ See footnote 220 at page 257 and footnote 221 at page 259
|
||||
|
||||
// 7.18.2.1 Limits of exact-width integer types
|
||||
#define INT8_MIN ((int8_t)_I8_MIN)
|
||||
#define INT8_MAX _I8_MAX
|
||||
#define INT16_MIN ((int16_t)_I16_MIN)
|
||||
#define INT16_MAX _I16_MAX
|
||||
#define INT32_MIN ((int32_t)_I32_MIN)
|
||||
#define INT32_MAX _I32_MAX
|
||||
#define INT64_MIN ((int64_t)_I64_MIN)
|
||||
#define INT64_MAX _I64_MAX
|
||||
#define UINT8_MAX _UI8_MAX
|
||||
#define UINT16_MAX _UI16_MAX
|
||||
#define UINT32_MAX _UI32_MAX
|
||||
#define UINT64_MAX _UI64_MAX
|
||||
|
||||
// 7.18.2.2 Limits of minimum-width integer types
|
||||
#define INT_LEAST8_MIN INT8_MIN
|
||||
#define INT_LEAST8_MAX INT8_MAX
|
||||
#define INT_LEAST16_MIN INT16_MIN
|
||||
#define INT_LEAST16_MAX INT16_MAX
|
||||
#define INT_LEAST32_MIN INT32_MIN
|
||||
#define INT_LEAST32_MAX INT32_MAX
|
||||
#define INT_LEAST64_MIN INT64_MIN
|
||||
#define INT_LEAST64_MAX INT64_MAX
|
||||
#define UINT_LEAST8_MAX UINT8_MAX
|
||||
#define UINT_LEAST16_MAX UINT16_MAX
|
||||
#define UINT_LEAST32_MAX UINT32_MAX
|
||||
#define UINT_LEAST64_MAX UINT64_MAX
|
||||
|
||||
// 7.18.2.3 Limits of fastest minimum-width integer types
|
||||
#define INT_FAST8_MIN INT8_MIN
|
||||
#define INT_FAST8_MAX INT8_MAX
|
||||
#define INT_FAST16_MIN INT16_MIN
|
||||
#define INT_FAST16_MAX INT16_MAX
|
||||
#define INT_FAST32_MIN INT32_MIN
|
||||
#define INT_FAST32_MAX INT32_MAX
|
||||
#define INT_FAST64_MIN INT64_MIN
|
||||
#define INT_FAST64_MAX INT64_MAX
|
||||
#define UINT_FAST8_MAX UINT8_MAX
|
||||
#define UINT_FAST16_MAX UINT16_MAX
|
||||
#define UINT_FAST32_MAX UINT32_MAX
|
||||
#define UINT_FAST64_MAX UINT64_MAX
|
||||
|
||||
// 7.18.2.4 Limits of integer types capable of holding object pointers
|
||||
#ifdef _WIN64 // [
|
||||
# define INTPTR_MIN INT64_MIN
|
||||
# define INTPTR_MAX INT64_MAX
|
||||
# define UINTPTR_MAX UINT64_MAX
|
||||
#else // _WIN64 ][
|
||||
# define INTPTR_MIN INT32_MIN
|
||||
# define INTPTR_MAX INT32_MAX
|
||||
# define UINTPTR_MAX UINT32_MAX
|
||||
#endif // _WIN64 ]
|
||||
|
||||
// 7.18.2.5 Limits of greatest-width integer types
|
||||
#define INTMAX_MIN INT64_MIN
|
||||
#define INTMAX_MAX INT64_MAX
|
||||
#define UINTMAX_MAX UINT64_MAX
|
||||
|
||||
// 7.18.3 Limits of other integer types
|
||||
|
||||
#ifdef _WIN64 // [
|
||||
# define PTRDIFF_MIN _I64_MIN
|
||||
# define PTRDIFF_MAX _I64_MAX
|
||||
#else // _WIN64 ][
|
||||
# define PTRDIFF_MIN _I32_MIN
|
||||
# define PTRDIFF_MAX _I32_MAX
|
||||
#endif // _WIN64 ]
|
||||
|
||||
#define SIG_ATOMIC_MIN INT_MIN
|
||||
#define SIG_ATOMIC_MAX INT_MAX
|
||||
|
||||
#ifndef SIZE_MAX // [
|
||||
# ifdef _WIN64 // [
|
||||
# define SIZE_MAX _UI64_MAX
|
||||
# else // _WIN64 ][
|
||||
# define SIZE_MAX _UI32_MAX
|
||||
# endif // _WIN64 ]
|
||||
#endif // SIZE_MAX ]
|
||||
|
||||
// WCHAR_MIN and WCHAR_MAX are also defined in <wchar.h>
|
||||
#ifndef WCHAR_MIN // [
|
||||
# define WCHAR_MIN 0
|
||||
#endif // WCHAR_MIN ]
|
||||
#ifndef WCHAR_MAX // [
|
||||
# define WCHAR_MAX _UI16_MAX
|
||||
#endif // WCHAR_MAX ]
|
||||
|
||||
#define WINT_MIN 0
|
||||
#define WINT_MAX _UI16_MAX
|
||||
|
||||
#endif // __STDC_LIMIT_MACROS ]
|
||||
|
||||
|
||||
// 7.18.4 Limits of other integer types
|
||||
|
||||
#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260
|
||||
|
||||
// 7.18.4.1 Macros for minimum-width integer constants
|
||||
|
||||
#define INT8_C(val) val##i8
|
||||
#define INT16_C(val) val##i16
|
||||
#define INT32_C(val) val##i32
|
||||
#define INT64_C(val) val##i64
|
||||
|
||||
#define UINT8_C(val) val##ui8
|
||||
#define UINT16_C(val) val##ui16
|
||||
#define UINT32_C(val) val##ui32
|
||||
#define UINT64_C(val) val##ui64
|
||||
|
||||
// 7.18.4.2 Macros for greatest-width integer constants
|
||||
#define INTMAX_C INT64_C
|
||||
#define UINTMAX_C UINT64_C
|
||||
|
||||
#endif // __STDC_CONSTANT_MACROS ]
|
||||
|
||||
|
||||
#endif // _MSC_STDINT_H_ ]
|
||||
@@ -0,0 +1,366 @@
|
||||
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "uv-common.h"
|
||||
|
||||
#include "../libs/pilight/core/pilight.h"
|
||||
#include "../libs/pilight/core/proc.h"
|
||||
|
||||
#if !defined(_WIN32)
|
||||
# include "unix/internal.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#define MAX_THREADPOOL_SIZE 128
|
||||
|
||||
static uv_once_t once = UV_ONCE_INIT;
|
||||
static uv_cond_t cond;
|
||||
static uv_mutex_t mutex;
|
||||
static unsigned int idle_threads;
|
||||
static unsigned int nthreads;
|
||||
static uv_thread_t* threads;
|
||||
static uv_thread_t default_threads[4];
|
||||
static QUEUE exit_message;
|
||||
static QUEUE wq;
|
||||
static volatile int initialized;
|
||||
|
||||
typedef struct data_t {
|
||||
int nr;
|
||||
|
||||
struct {
|
||||
struct timespec first;
|
||||
struct timespec second;
|
||||
} timestamp;
|
||||
|
||||
struct cpu_usage_t cpu_usage;
|
||||
} data_t;
|
||||
|
||||
static void uv__cancelled(struct uv__work* w) {
|
||||
abort();
|
||||
}
|
||||
|
||||
|
||||
/* To avoid deadlock with uv_cancel() it's crucial that the worker
|
||||
* never holds the global mutex and the loop-local mutex at the same time.
|
||||
*/
|
||||
static void worker(void* arg) {
|
||||
struct uv__work* w;
|
||||
QUEUE* q;
|
||||
|
||||
#ifndef _WIN32
|
||||
struct data_t *data = arg;
|
||||
#endif
|
||||
|
||||
for (;;) {
|
||||
uv_mutex_lock(&mutex);
|
||||
|
||||
while (QUEUE_EMPTY(&wq)) {
|
||||
idle_threads += 1;
|
||||
uv_cond_wait(&cond, &mutex);
|
||||
idle_threads -= 1;
|
||||
}
|
||||
|
||||
q = QUEUE_HEAD(&wq);
|
||||
|
||||
if (q == &exit_message)
|
||||
uv_cond_signal(&cond);
|
||||
else {
|
||||
QUEUE_REMOVE(q);
|
||||
QUEUE_INIT(q); /* Signal uv_cancel() that the work req is
|
||||
executing. */
|
||||
}
|
||||
|
||||
uv_mutex_unlock(&mutex);
|
||||
|
||||
if (q == &exit_message)
|
||||
break;
|
||||
|
||||
w = QUEUE_DATA(q, struct uv__work, wq);
|
||||
#ifndef _WIN32
|
||||
if(pilight.debuglevel >= 2) {
|
||||
getThreadCPUUsage(pthread_self(), &data->cpu_usage);
|
||||
clock_gettime(CLOCK_MONOTONIC, &data->timestamp.first);
|
||||
}
|
||||
#endif
|
||||
w->work(w);
|
||||
|
||||
#ifndef _WIN32
|
||||
if(pilight.debuglevel >= 2) {
|
||||
clock_gettime(CLOCK_MONOTONIC, &data->timestamp.second);
|
||||
getThreadCPUUsage(pthread_self(), &data->cpu_usage);
|
||||
fprintf(stderr, "worker %d, executed %s in %.6f sec using %f%% CPU\n",
|
||||
data->nr,
|
||||
w->name,
|
||||
((double)data->timestamp.second.tv_sec + 1.0e-9*data->timestamp.second.tv_nsec) -
|
||||
((double)data->timestamp.first.tv_sec + 1.0e-9*data->timestamp.first.tv_nsec),
|
||||
data->cpu_usage.cpu_per
|
||||
);
|
||||
}
|
||||
#endif
|
||||
|
||||
// free(w->name);
|
||||
uv_mutex_lock(&w->loop->wq_mutex);
|
||||
w->work = NULL; /* Signal uv_cancel() that the work req is done
|
||||
executing. */
|
||||
QUEUE_INSERT_TAIL(&w->loop->wq, &w->wq);
|
||||
uv_async_send(&w->loop->wq_async);
|
||||
uv_mutex_unlock(&w->loop->wq_mutex);
|
||||
}
|
||||
#ifndef _WIN32
|
||||
free(data);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
static void post(QUEUE* q) {
|
||||
uv_mutex_lock(&mutex);
|
||||
QUEUE_INSERT_TAIL(&wq, q);
|
||||
if (idle_threads > 0)
|
||||
uv_cond_signal(&cond);
|
||||
uv_mutex_unlock(&mutex);
|
||||
}
|
||||
|
||||
|
||||
#ifndef _WIN32
|
||||
UV_DESTRUCTOR(static void cleanup(void)) {
|
||||
unsigned int i;
|
||||
|
||||
if (initialized == 0)
|
||||
return;
|
||||
|
||||
post(&exit_message);
|
||||
|
||||
for (i = 0; i < nthreads; i++)
|
||||
if (uv_thread_join(threads + i))
|
||||
abort();
|
||||
|
||||
if (threads != default_threads)
|
||||
uv__free(threads);
|
||||
|
||||
uv_mutex_destroy(&mutex);
|
||||
uv_cond_destroy(&cond);
|
||||
|
||||
threads = NULL;
|
||||
nthreads = 0;
|
||||
initialized = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
static void init_threads(void) {
|
||||
unsigned int i;
|
||||
const char* val;
|
||||
|
||||
nthreads = ARRAY_SIZE(default_threads);
|
||||
val = getenv("UV_THREADPOOL_SIZE");
|
||||
if (val != NULL)
|
||||
nthreads = atoi(val);
|
||||
if (nthreads == 0)
|
||||
nthreads = 1;
|
||||
if (nthreads > MAX_THREADPOOL_SIZE)
|
||||
nthreads = MAX_THREADPOOL_SIZE;
|
||||
|
||||
threads = default_threads;
|
||||
if (nthreads > ARRAY_SIZE(default_threads)) {
|
||||
threads = uv__malloc(nthreads * sizeof(threads[0]));
|
||||
if (threads == NULL) {
|
||||
nthreads = ARRAY_SIZE(default_threads);
|
||||
threads = default_threads;
|
||||
}
|
||||
}
|
||||
|
||||
if (uv_cond_init(&cond))
|
||||
abort();
|
||||
|
||||
if (uv_mutex_init(&mutex))
|
||||
abort();
|
||||
|
||||
QUEUE_INIT(&wq);
|
||||
|
||||
for (i = 0; i < nthreads; i++) {
|
||||
#ifndef _WIN32
|
||||
struct data_t *data = malloc(sizeof(struct data_t));
|
||||
memset(data, '\0', sizeof(struct data_t));
|
||||
data->nr = i;
|
||||
if (uv_thread_create(threads + i, worker, data))
|
||||
abort();
|
||||
#else
|
||||
if (uv_thread_create(threads + i, worker, NULL))
|
||||
abort();
|
||||
#endif
|
||||
}
|
||||
|
||||
initialized = 1;
|
||||
}
|
||||
|
||||
|
||||
#ifndef _WIN32
|
||||
static void reset_once(void) {
|
||||
uv_once_t child_once = UV_ONCE_INIT;
|
||||
memcpy(&once, &child_once, sizeof(child_once));
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
static void init_once(void) {
|
||||
#ifndef _WIN32
|
||||
/* Re-initialize the threadpool after fork.
|
||||
* Note that this discards the global mutex and condition as well
|
||||
* as the work queue.
|
||||
*/
|
||||
if (pthread_atfork(NULL, NULL, &reset_once))
|
||||
abort();
|
||||
#endif
|
||||
init_threads();
|
||||
}
|
||||
|
||||
|
||||
void uv__work_submit(uv_loop_t* loop,
|
||||
struct uv__work* w,
|
||||
void (*work)(struct uv__work* w),
|
||||
void (*done)(struct uv__work* w, int status)) {
|
||||
uv_once(&once, init_once);
|
||||
w->loop = loop;
|
||||
w->work = work;
|
||||
w->done = done;
|
||||
post(&w->wq);
|
||||
}
|
||||
|
||||
|
||||
static int uv__work_cancel(uv_loop_t* loop, uv_req_t* req, struct uv__work* w) {
|
||||
int cancelled;
|
||||
|
||||
uv_mutex_lock(&mutex);
|
||||
uv_mutex_lock(&w->loop->wq_mutex);
|
||||
|
||||
cancelled = !QUEUE_EMPTY(&w->wq) && w->work != NULL;
|
||||
if (cancelled)
|
||||
QUEUE_REMOVE(&w->wq);
|
||||
|
||||
uv_mutex_unlock(&w->loop->wq_mutex);
|
||||
uv_mutex_unlock(&mutex);
|
||||
|
||||
if (!cancelled)
|
||||
return UV_EBUSY;
|
||||
|
||||
w->work = uv__cancelled;
|
||||
uv_mutex_lock(&loop->wq_mutex);
|
||||
QUEUE_INSERT_TAIL(&loop->wq, &w->wq);
|
||||
uv_async_send(&loop->wq_async);
|
||||
uv_mutex_unlock(&loop->wq_mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void uv__work_done(uv_async_t* handle) {
|
||||
struct uv__work* w;
|
||||
uv_loop_t* loop;
|
||||
QUEUE* q;
|
||||
QUEUE wq;
|
||||
int err;
|
||||
|
||||
loop = container_of(handle, uv_loop_t, wq_async);
|
||||
uv_mutex_lock(&loop->wq_mutex);
|
||||
QUEUE_MOVE(&loop->wq, &wq);
|
||||
uv_mutex_unlock(&loop->wq_mutex);
|
||||
|
||||
while (!QUEUE_EMPTY(&wq)) {
|
||||
q = QUEUE_HEAD(&wq);
|
||||
QUEUE_REMOVE(q);
|
||||
|
||||
w = container_of(q, struct uv__work, wq);
|
||||
err = (w->work == uv__cancelled) ? UV_ECANCELED : 0;
|
||||
w->done(w, err);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void uv__queue_work(struct uv__work* w) {
|
||||
uv_work_t* req = container_of(w, uv_work_t, work_req);
|
||||
|
||||
req->work_cb(req);
|
||||
}
|
||||
|
||||
|
||||
static void uv__queue_done(struct uv__work* w, int err) {
|
||||
uv_work_t* req;
|
||||
|
||||
req = container_of(w, uv_work_t, work_req);
|
||||
if(QUEUE_EMPTY(&(req->loop)->active_reqs) != 0) {
|
||||
printf("-- %s --\n", w->name);
|
||||
}
|
||||
free(w->name);
|
||||
uv__req_unregister(req->loop, req);
|
||||
|
||||
if (req->after_work_cb == NULL)
|
||||
return;
|
||||
|
||||
req->after_work_cb(req, err);
|
||||
}
|
||||
|
||||
|
||||
int uv_queue_work(uv_loop_t* loop,
|
||||
uv_work_t* req,
|
||||
char *name,
|
||||
uv_work_cb work_cb,
|
||||
uv_after_work_cb after_work_cb) {
|
||||
if (work_cb == NULL)
|
||||
return UV_EINVAL;
|
||||
|
||||
uv__req_init(loop, req, UV_WORK);
|
||||
req->loop = loop;
|
||||
req->work_cb = work_cb;
|
||||
req->after_work_cb = after_work_cb;
|
||||
req->work_req.name = strdup(name);
|
||||
uv__work_submit(loop, &req->work_req, uv__queue_work, uv__queue_done);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv_cancel(uv_req_t* req) {
|
||||
struct uv__work* wreq;
|
||||
uv_loop_t* loop;
|
||||
|
||||
switch (req->type) {
|
||||
case UV_FS:
|
||||
loop = ((uv_fs_t*) req)->loop;
|
||||
wreq = &((uv_fs_t*) req)->work_req;
|
||||
break;
|
||||
case UV_GETADDRINFO:
|
||||
loop = ((uv_getaddrinfo_t*) req)->loop;
|
||||
wreq = &((uv_getaddrinfo_t*) req)->work_req;
|
||||
break;
|
||||
case UV_GETNAMEINFO:
|
||||
loop = ((uv_getnameinfo_t*) req)->loop;
|
||||
wreq = &((uv_getnameinfo_t*) req)->work_req;
|
||||
break;
|
||||
case UV_WORK:
|
||||
loop = ((uv_work_t*) req)->loop;
|
||||
wreq = &((uv_work_t*) req)->work_req;
|
||||
break;
|
||||
default:
|
||||
return UV_EINVAL;
|
||||
}
|
||||
|
||||
return uv__work_cancel(loop, req, wreq);
|
||||
}
|
||||
@@ -0,0 +1,312 @@
|
||||
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "uv-common.h"
|
||||
|
||||
#if !defined(_WIN32)
|
||||
# include "unix/internal.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#define MAX_THREADPOOL_SIZE 128
|
||||
|
||||
static uv_once_t once = UV_ONCE_INIT;
|
||||
static uv_cond_t cond;
|
||||
static uv_mutex_t mutex;
|
||||
static unsigned int idle_threads;
|
||||
static unsigned int nthreads;
|
||||
static uv_thread_t* threads;
|
||||
static uv_thread_t default_threads[4];
|
||||
static QUEUE exit_message;
|
||||
static QUEUE wq;
|
||||
static volatile int initialized;
|
||||
|
||||
|
||||
static void uv__cancelled(struct uv__work* w) {
|
||||
abort();
|
||||
}
|
||||
|
||||
|
||||
/* To avoid deadlock with uv_cancel() it's crucial that the worker
|
||||
* never holds the global mutex and the loop-local mutex at the same time.
|
||||
*/
|
||||
static void worker(void* arg) {
|
||||
struct uv__work* w;
|
||||
QUEUE* q;
|
||||
|
||||
(void) arg;
|
||||
|
||||
for (;;) {
|
||||
uv_mutex_lock(&mutex);
|
||||
|
||||
while (QUEUE_EMPTY(&wq)) {
|
||||
idle_threads += 1;
|
||||
uv_cond_wait(&cond, &mutex);
|
||||
idle_threads -= 1;
|
||||
}
|
||||
|
||||
q = QUEUE_HEAD(&wq);
|
||||
|
||||
if (q == &exit_message)
|
||||
uv_cond_signal(&cond);
|
||||
else {
|
||||
QUEUE_REMOVE(q);
|
||||
QUEUE_INIT(q); /* Signal uv_cancel() that the work req is
|
||||
executing. */
|
||||
}
|
||||
|
||||
uv_mutex_unlock(&mutex);
|
||||
|
||||
if (q == &exit_message)
|
||||
break;
|
||||
|
||||
w = QUEUE_DATA(q, struct uv__work, wq);
|
||||
w->work(w);
|
||||
|
||||
uv_mutex_lock(&w->loop->wq_mutex);
|
||||
w->work = NULL; /* Signal uv_cancel() that the work req is done
|
||||
executing. */
|
||||
QUEUE_INSERT_TAIL(&w->loop->wq, &w->wq);
|
||||
uv_async_send(&w->loop->wq_async);
|
||||
uv_mutex_unlock(&w->loop->wq_mutex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void post(QUEUE* q) {
|
||||
uv_mutex_lock(&mutex);
|
||||
QUEUE_INSERT_TAIL(&wq, q);
|
||||
if (idle_threads > 0)
|
||||
uv_cond_signal(&cond);
|
||||
uv_mutex_unlock(&mutex);
|
||||
}
|
||||
|
||||
|
||||
#ifndef _WIN32
|
||||
UV_DESTRUCTOR(static void cleanup(void)) {
|
||||
unsigned int i;
|
||||
|
||||
if (initialized == 0)
|
||||
return;
|
||||
|
||||
post(&exit_message);
|
||||
|
||||
for (i = 0; i < nthreads; i++)
|
||||
if (uv_thread_join(threads + i))
|
||||
abort();
|
||||
|
||||
if (threads != default_threads)
|
||||
uv__free(threads);
|
||||
|
||||
uv_mutex_destroy(&mutex);
|
||||
uv_cond_destroy(&cond);
|
||||
|
||||
threads = NULL;
|
||||
nthreads = 0;
|
||||
initialized = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
static void init_threads(void) {
|
||||
unsigned int i;
|
||||
const char* val;
|
||||
|
||||
nthreads = ARRAY_SIZE(default_threads);
|
||||
val = getenv("UV_THREADPOOL_SIZE");
|
||||
if (val != NULL)
|
||||
nthreads = atoi(val);
|
||||
if (nthreads == 0)
|
||||
nthreads = 1;
|
||||
if (nthreads > MAX_THREADPOOL_SIZE)
|
||||
nthreads = MAX_THREADPOOL_SIZE;
|
||||
|
||||
threads = default_threads;
|
||||
if (nthreads > ARRAY_SIZE(default_threads)) {
|
||||
threads = uv__malloc(nthreads * sizeof(threads[0]));
|
||||
if (threads == NULL) {
|
||||
nthreads = ARRAY_SIZE(default_threads);
|
||||
threads = default_threads;
|
||||
}
|
||||
}
|
||||
|
||||
if (uv_cond_init(&cond))
|
||||
abort();
|
||||
|
||||
if (uv_mutex_init(&mutex))
|
||||
abort();
|
||||
|
||||
QUEUE_INIT(&wq);
|
||||
|
||||
for (i = 0; i < nthreads; i++)
|
||||
if (uv_thread_create(threads + i, worker, NULL))
|
||||
abort();
|
||||
|
||||
initialized = 1;
|
||||
}
|
||||
|
||||
|
||||
#ifndef _WIN32
|
||||
static void reset_once(void) {
|
||||
uv_once_t child_once = UV_ONCE_INIT;
|
||||
memcpy(&once, &child_once, sizeof(child_once));
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
static void init_once(void) {
|
||||
#ifndef _WIN32
|
||||
/* Re-initialize the threadpool after fork.
|
||||
* Note that this discards the global mutex and condition as well
|
||||
* as the work queue.
|
||||
*/
|
||||
if (pthread_atfork(NULL, NULL, &reset_once))
|
||||
abort();
|
||||
#endif
|
||||
init_threads();
|
||||
}
|
||||
|
||||
|
||||
void uv__work_submit(uv_loop_t* loop,
|
||||
struct uv__work* w,
|
||||
void (*work)(struct uv__work* w),
|
||||
void (*done)(struct uv__work* w, int status)) {
|
||||
uv_once(&once, init_once);
|
||||
w->loop = loop;
|
||||
w->work = work;
|
||||
w->done = done;
|
||||
post(&w->wq);
|
||||
}
|
||||
|
||||
|
||||
static int uv__work_cancel(uv_loop_t* loop, uv_req_t* req, struct uv__work* w) {
|
||||
int cancelled;
|
||||
|
||||
uv_mutex_lock(&mutex);
|
||||
uv_mutex_lock(&w->loop->wq_mutex);
|
||||
|
||||
cancelled = !QUEUE_EMPTY(&w->wq) && w->work != NULL;
|
||||
if (cancelled)
|
||||
QUEUE_REMOVE(&w->wq);
|
||||
|
||||
uv_mutex_unlock(&w->loop->wq_mutex);
|
||||
uv_mutex_unlock(&mutex);
|
||||
|
||||
if (!cancelled)
|
||||
return UV_EBUSY;
|
||||
|
||||
w->work = uv__cancelled;
|
||||
uv_mutex_lock(&loop->wq_mutex);
|
||||
QUEUE_INSERT_TAIL(&loop->wq, &w->wq);
|
||||
uv_async_send(&loop->wq_async);
|
||||
uv_mutex_unlock(&loop->wq_mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void uv__work_done(uv_async_t* handle) {
|
||||
struct uv__work* w;
|
||||
uv_loop_t* loop;
|
||||
QUEUE* q;
|
||||
QUEUE wq;
|
||||
int err;
|
||||
|
||||
loop = container_of(handle, uv_loop_t, wq_async);
|
||||
uv_mutex_lock(&loop->wq_mutex);
|
||||
QUEUE_MOVE(&loop->wq, &wq);
|
||||
uv_mutex_unlock(&loop->wq_mutex);
|
||||
|
||||
while (!QUEUE_EMPTY(&wq)) {
|
||||
q = QUEUE_HEAD(&wq);
|
||||
QUEUE_REMOVE(q);
|
||||
|
||||
w = container_of(q, struct uv__work, wq);
|
||||
err = (w->work == uv__cancelled) ? UV_ECANCELED : 0;
|
||||
w->done(w, err);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void uv__queue_work(struct uv__work* w) {
|
||||
uv_work_t* req = container_of(w, uv_work_t, work_req);
|
||||
|
||||
req->work_cb(req);
|
||||
}
|
||||
|
||||
|
||||
static void uv__queue_done(struct uv__work* w, int err) {
|
||||
uv_work_t* req;
|
||||
|
||||
req = container_of(w, uv_work_t, work_req);
|
||||
uv__req_unregister(req->loop, req);
|
||||
|
||||
if (req->after_work_cb == NULL)
|
||||
return;
|
||||
|
||||
req->after_work_cb(req, err);
|
||||
}
|
||||
|
||||
|
||||
int uv_queue_work(uv_loop_t* loop,
|
||||
uv_work_t* req,
|
||||
uv_work_cb work_cb,
|
||||
uv_after_work_cb after_work_cb) {
|
||||
if (work_cb == NULL)
|
||||
return UV_EINVAL;
|
||||
|
||||
uv__req_init(loop, req, UV_WORK);
|
||||
req->loop = loop;
|
||||
req->work_cb = work_cb;
|
||||
req->after_work_cb = after_work_cb;
|
||||
uv__work_submit(loop, &req->work_req, uv__queue_work, uv__queue_done);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv_cancel(uv_req_t* req) {
|
||||
struct uv__work* wreq;
|
||||
uv_loop_t* loop;
|
||||
|
||||
switch (req->type) {
|
||||
case UV_FS:
|
||||
loop = ((uv_fs_t*) req)->loop;
|
||||
wreq = &((uv_fs_t*) req)->work_req;
|
||||
break;
|
||||
case UV_GETADDRINFO:
|
||||
loop = ((uv_getaddrinfo_t*) req)->loop;
|
||||
wreq = &((uv_getaddrinfo_t*) req)->work_req;
|
||||
break;
|
||||
case UV_GETNAMEINFO:
|
||||
loop = ((uv_getnameinfo_t*) req)->loop;
|
||||
wreq = &((uv_getnameinfo_t*) req)->work_req;
|
||||
break;
|
||||
case UV_WORK:
|
||||
loop = ((uv_work_t*) req)->loop;
|
||||
wreq = &((uv_work_t*) req)->work_req;
|
||||
break;
|
||||
default:
|
||||
return UV_EINVAL;
|
||||
}
|
||||
|
||||
return uv__work_cancel(loop, req, wreq);
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
--- uv.h 2017-06-27 11:20:03.336712000 +0200
|
||||
+++ uv.h 2017-06-27 11:32:42.786153181 +0200
|
||||
@@ -974,6 +974,7 @@
|
||||
|
||||
UV_EXTERN int uv_queue_work(uv_loop_t* loop,
|
||||
uv_work_t* req,
|
||||
+ char *name,
|
||||
uv_work_cb work_cb,
|
||||
uv_after_work_cb after_work_cb);
|
||||
|
||||
--- uv-threadpool.h 2017-06-27 11:15:32.716593000 +0200
|
||||
+++ uv-threadpool.h 2017-06-27 11:34:19.966961233 +0200
|
||||
@@ -28,6 +28,7 @@
|
||||
#define UV_THREADPOOL_H_
|
||||
|
||||
struct uv__work {
|
||||
+ char *name;
|
||||
void (*work)(struct uv__work *w);
|
||||
void (*done)(struct uv__work *w, int status);
|
||||
struct uv_loop_s* loop;
|
||||
--- threadpool.c 2017-06-27 11:15:32.719162000 +0200
|
||||
+++ threadpool.c 2017-06-27 11:33:06.220654218 +0200
|
||||
@@ -21,6 +21,9 @@
|
||||
|
||||
#include "uv-common.h"
|
||||
|
||||
+#include "../libs/pilight/core/pilight.h"
|
||||
+#include "../libs/pilight/core/proc.h"
|
||||
+
|
||||
#if !defined(_WIN32)
|
||||
# include "unix/internal.h"
|
||||
#endif
|
||||
@@ -40,6 +43,16 @@
|
||||
static QUEUE wq;
|
||||
static volatile int initialized;
|
||||
|
||||
+typedef struct data_t {
|
||||
+ int nr;
|
||||
+
|
||||
+ struct {
|
||||
+ struct timespec first;
|
||||
+ struct timespec second;
|
||||
+ } timestamp;
|
||||
+
|
||||
+ struct cpu_usage_t cpu_usage;
|
||||
+} data_t;
|
||||
|
||||
static void uv__cancelled(struct uv__work* w) {
|
||||
abort();
|
||||
@@ -53,7 +66,9 @@
|
||||
struct uv__work* w;
|
||||
QUEUE* q;
|
||||
|
||||
- (void) arg;
|
||||
+#ifndef _WIN32
|
||||
+ struct data_t *data = arg;
|
||||
+#endif
|
||||
|
||||
for (;;) {
|
||||
uv_mutex_lock(&mutex);
|
||||
@@ -80,8 +95,29 @@
|
||||
break;
|
||||
|
||||
w = QUEUE_DATA(q, struct uv__work, wq);
|
||||
+#ifndef _WIN32
|
||||
+ if(pilight.debuglevel >= 2) {
|
||||
+ getThreadCPUUsage(pthread_self(), &data->cpu_usage);
|
||||
+ clock_gettime(CLOCK_MONOTONIC, &data->timestamp.first);
|
||||
+ }
|
||||
+#endif
|
||||
w->work(w);
|
||||
|
||||
+#ifndef _WIN32
|
||||
+ if(pilight.debuglevel >= 2) {
|
||||
+ clock_gettime(CLOCK_MONOTONIC, &data->timestamp.second);
|
||||
+ getThreadCPUUsage(pthread_self(), &data->cpu_usage);
|
||||
+ fprintf(stderr, "worker %d, executed %s in %.6f sec using %f%% CPU\n",
|
||||
+ data->nr,
|
||||
+ w->name,
|
||||
+ ((double)data->timestamp.second.tv_sec + 1.0e-9*data->timestamp.second.tv_nsec) -
|
||||
+ ((double)data->timestamp.first.tv_sec + 1.0e-9*data->timestamp.first.tv_nsec),
|
||||
+ data->cpu_usage.cpu_per
|
||||
+ );
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
+ free(w->name);
|
||||
uv_mutex_lock(&w->loop->wq_mutex);
|
||||
w->work = NULL; /* Signal uv_cancel() that the work req is done
|
||||
executing. */
|
||||
@@ -89,6 +125,9 @@
|
||||
uv_async_send(&w->loop->wq_async);
|
||||
uv_mutex_unlock(&w->loop->wq_mutex);
|
||||
}
|
||||
+#ifndef _WIN32
|
||||
+ free(data);
|
||||
+#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -157,9 +196,18 @@
|
||||
|
||||
QUEUE_INIT(&wq);
|
||||
|
||||
- for (i = 0; i < nthreads; i++)
|
||||
+ for (i = 0; i < nthreads; i++) {
|
||||
+#ifndef _WIN32
|
||||
+ struct data_t *data = malloc(sizeof(struct data_t));
|
||||
+ memset(data, '\0', sizeof(struct data_t));
|
||||
+ data->nr = i;
|
||||
+ if (uv_thread_create(threads + i, worker, data))
|
||||
+ abort();
|
||||
+#else
|
||||
if (uv_thread_create(threads + i, worker, NULL))
|
||||
abort();
|
||||
+#endif
|
||||
+ }
|
||||
|
||||
initialized = 1;
|
||||
}
|
||||
@@ -269,6 +317,7 @@
|
||||
|
||||
int uv_queue_work(uv_loop_t* loop,
|
||||
uv_work_t* req,
|
||||
+ char *name,
|
||||
uv_work_cb work_cb,
|
||||
uv_after_work_cb after_work_cb) {
|
||||
if (work_cb == NULL)
|
||||
@@ -278,6 +327,7 @@
|
||||
req->loop = loop;
|
||||
req->work_cb = work_cb;
|
||||
req->after_work_cb = after_work_cb;
|
||||
+ req->work_req.name = strdup(name);
|
||||
uv__work_submit(loop, &req->work_req, uv__queue_work, uv__queue_done);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,768 @@
|
||||
/*-
|
||||
* Copyright 2002 Niels Provos <provos@citi.umich.edu>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. 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.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
|
||||
*/
|
||||
|
||||
#ifndef UV_TREE_H_
|
||||
#define UV_TREE_H_
|
||||
|
||||
#ifndef UV__UNUSED
|
||||
# if __GNUC__
|
||||
# define UV__UNUSED __attribute__((unused))
|
||||
# else
|
||||
# define UV__UNUSED
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This file defines data structures for different types of trees:
|
||||
* splay trees and red-black trees.
|
||||
*
|
||||
* A splay tree is a self-organizing data structure. Every operation
|
||||
* on the tree causes a splay to happen. The splay moves the requested
|
||||
* node to the root of the tree and partly rebalances it.
|
||||
*
|
||||
* This has the benefit that request locality causes faster lookups as
|
||||
* the requested nodes move to the top of the tree. On the other hand,
|
||||
* every lookup causes memory writes.
|
||||
*
|
||||
* The Balance Theorem bounds the total access time for m operations
|
||||
* and n inserts on an initially empty tree as O((m + n)lg n). The
|
||||
* amortized cost for a sequence of m accesses to a splay tree is O(lg n);
|
||||
*
|
||||
* A red-black tree is a binary search tree with the node color as an
|
||||
* extra attribute. It fulfills a set of conditions:
|
||||
* - every search path from the root to a leaf consists of the
|
||||
* same number of black nodes,
|
||||
* - each red node (except for the root) has a black parent,
|
||||
* - each leaf node is black.
|
||||
*
|
||||
* Every operation on a red-black tree is bounded as O(lg n).
|
||||
* The maximum height of a red-black tree is 2lg (n+1).
|
||||
*/
|
||||
|
||||
#define SPLAY_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *sph_root; /* root of the tree */ \
|
||||
}
|
||||
|
||||
#define SPLAY_INITIALIZER(root) \
|
||||
{ NULL }
|
||||
|
||||
#define SPLAY_INIT(root) do { \
|
||||
(root)->sph_root = NULL; \
|
||||
} while (/*CONSTCOND*/ 0)
|
||||
|
||||
#define SPLAY_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *spe_left; /* left element */ \
|
||||
struct type *spe_right; /* right element */ \
|
||||
}
|
||||
|
||||
#define SPLAY_LEFT(elm, field) (elm)->field.spe_left
|
||||
#define SPLAY_RIGHT(elm, field) (elm)->field.spe_right
|
||||
#define SPLAY_ROOT(head) (head)->sph_root
|
||||
#define SPLAY_EMPTY(head) (SPLAY_ROOT(head) == NULL)
|
||||
|
||||
/* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */
|
||||
#define SPLAY_ROTATE_RIGHT(head, tmp, field) do { \
|
||||
SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field); \
|
||||
SPLAY_RIGHT(tmp, field) = (head)->sph_root; \
|
||||
(head)->sph_root = tmp; \
|
||||
} while (/*CONSTCOND*/ 0)
|
||||
|
||||
#define SPLAY_ROTATE_LEFT(head, tmp, field) do { \
|
||||
SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field); \
|
||||
SPLAY_LEFT(tmp, field) = (head)->sph_root; \
|
||||
(head)->sph_root = tmp; \
|
||||
} while (/*CONSTCOND*/ 0)
|
||||
|
||||
#define SPLAY_LINKLEFT(head, tmp, field) do { \
|
||||
SPLAY_LEFT(tmp, field) = (head)->sph_root; \
|
||||
tmp = (head)->sph_root; \
|
||||
(head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \
|
||||
} while (/*CONSTCOND*/ 0)
|
||||
|
||||
#define SPLAY_LINKRIGHT(head, tmp, field) do { \
|
||||
SPLAY_RIGHT(tmp, field) = (head)->sph_root; \
|
||||
tmp = (head)->sph_root; \
|
||||
(head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \
|
||||
} while (/*CONSTCOND*/ 0)
|
||||
|
||||
#define SPLAY_ASSEMBLE(head, node, left, right, field) do { \
|
||||
SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field); \
|
||||
SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field); \
|
||||
SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field); \
|
||||
SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field); \
|
||||
} while (/*CONSTCOND*/ 0)
|
||||
|
||||
/* Generates prototypes and inline functions */
|
||||
|
||||
#define SPLAY_PROTOTYPE(name, type, field, cmp) \
|
||||
void name##_SPLAY(struct name *, struct type *); \
|
||||
void name##_SPLAY_MINMAX(struct name *, int); \
|
||||
struct type *name##_SPLAY_INSERT(struct name *, struct type *); \
|
||||
struct type *name##_SPLAY_REMOVE(struct name *, struct type *); \
|
||||
\
|
||||
/* Finds the node with the same key as elm */ \
|
||||
static __inline struct type * \
|
||||
name##_SPLAY_FIND(struct name *head, struct type *elm) \
|
||||
{ \
|
||||
if (SPLAY_EMPTY(head)) \
|
||||
return(NULL); \
|
||||
name##_SPLAY(head, elm); \
|
||||
if ((cmp)(elm, (head)->sph_root) == 0) \
|
||||
return (head->sph_root); \
|
||||
return (NULL); \
|
||||
} \
|
||||
\
|
||||
static __inline struct type * \
|
||||
name##_SPLAY_NEXT(struct name *head, struct type *elm) \
|
||||
{ \
|
||||
name##_SPLAY(head, elm); \
|
||||
if (SPLAY_RIGHT(elm, field) != NULL) { \
|
||||
elm = SPLAY_RIGHT(elm, field); \
|
||||
while (SPLAY_LEFT(elm, field) != NULL) { \
|
||||
elm = SPLAY_LEFT(elm, field); \
|
||||
} \
|
||||
} else \
|
||||
elm = NULL; \
|
||||
return (elm); \
|
||||
} \
|
||||
\
|
||||
static __inline struct type * \
|
||||
name##_SPLAY_MIN_MAX(struct name *head, int val) \
|
||||
{ \
|
||||
name##_SPLAY_MINMAX(head, val); \
|
||||
return (SPLAY_ROOT(head)); \
|
||||
}
|
||||
|
||||
/* Main splay operation.
|
||||
* Moves node close to the key of elm to top
|
||||
*/
|
||||
#define SPLAY_GENERATE(name, type, field, cmp) \
|
||||
struct type * \
|
||||
name##_SPLAY_INSERT(struct name *head, struct type *elm) \
|
||||
{ \
|
||||
if (SPLAY_EMPTY(head)) { \
|
||||
SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL; \
|
||||
} else { \
|
||||
int __comp; \
|
||||
name##_SPLAY(head, elm); \
|
||||
__comp = (cmp)(elm, (head)->sph_root); \
|
||||
if(__comp < 0) { \
|
||||
SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field); \
|
||||
SPLAY_RIGHT(elm, field) = (head)->sph_root; \
|
||||
SPLAY_LEFT((head)->sph_root, field) = NULL; \
|
||||
} else if (__comp > 0) { \
|
||||
SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field); \
|
||||
SPLAY_LEFT(elm, field) = (head)->sph_root; \
|
||||
SPLAY_RIGHT((head)->sph_root, field) = NULL; \
|
||||
} else \
|
||||
return ((head)->sph_root); \
|
||||
} \
|
||||
(head)->sph_root = (elm); \
|
||||
return (NULL); \
|
||||
} \
|
||||
\
|
||||
struct type * \
|
||||
name##_SPLAY_REMOVE(struct name *head, struct type *elm) \
|
||||
{ \
|
||||
struct type *__tmp; \
|
||||
if (SPLAY_EMPTY(head)) \
|
||||
return (NULL); \
|
||||
name##_SPLAY(head, elm); \
|
||||
if ((cmp)(elm, (head)->sph_root) == 0) { \
|
||||
if (SPLAY_LEFT((head)->sph_root, field) == NULL) { \
|
||||
(head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \
|
||||
} else { \
|
||||
__tmp = SPLAY_RIGHT((head)->sph_root, field); \
|
||||
(head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \
|
||||
name##_SPLAY(head, elm); \
|
||||
SPLAY_RIGHT((head)->sph_root, field) = __tmp; \
|
||||
} \
|
||||
return (elm); \
|
||||
} \
|
||||
return (NULL); \
|
||||
} \
|
||||
\
|
||||
void \
|
||||
name##_SPLAY(struct name *head, struct type *elm) \
|
||||
{ \
|
||||
struct type __node, *__left, *__right, *__tmp; \
|
||||
int __comp; \
|
||||
\
|
||||
SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL; \
|
||||
__left = __right = &__node; \
|
||||
\
|
||||
while ((__comp = (cmp)(elm, (head)->sph_root)) != 0) { \
|
||||
if (__comp < 0) { \
|
||||
__tmp = SPLAY_LEFT((head)->sph_root, field); \
|
||||
if (__tmp == NULL) \
|
||||
break; \
|
||||
if ((cmp)(elm, __tmp) < 0){ \
|
||||
SPLAY_ROTATE_RIGHT(head, __tmp, field); \
|
||||
if (SPLAY_LEFT((head)->sph_root, field) == NULL) \
|
||||
break; \
|
||||
} \
|
||||
SPLAY_LINKLEFT(head, __right, field); \
|
||||
} else if (__comp > 0) { \
|
||||
__tmp = SPLAY_RIGHT((head)->sph_root, field); \
|
||||
if (__tmp == NULL) \
|
||||
break; \
|
||||
if ((cmp)(elm, __tmp) > 0){ \
|
||||
SPLAY_ROTATE_LEFT(head, __tmp, field); \
|
||||
if (SPLAY_RIGHT((head)->sph_root, field) == NULL) \
|
||||
break; \
|
||||
} \
|
||||
SPLAY_LINKRIGHT(head, __left, field); \
|
||||
} \
|
||||
} \
|
||||
SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \
|
||||
} \
|
||||
\
|
||||
/* Splay with either the minimum or the maximum element \
|
||||
* Used to find minimum or maximum element in tree. \
|
||||
*/ \
|
||||
void name##_SPLAY_MINMAX(struct name *head, int __comp) \
|
||||
{ \
|
||||
struct type __node, *__left, *__right, *__tmp; \
|
||||
\
|
||||
SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL; \
|
||||
__left = __right = &__node; \
|
||||
\
|
||||
while (1) { \
|
||||
if (__comp < 0) { \
|
||||
__tmp = SPLAY_LEFT((head)->sph_root, field); \
|
||||
if (__tmp == NULL) \
|
||||
break; \
|
||||
if (__comp < 0){ \
|
||||
SPLAY_ROTATE_RIGHT(head, __tmp, field); \
|
||||
if (SPLAY_LEFT((head)->sph_root, field) == NULL) \
|
||||
break; \
|
||||
} \
|
||||
SPLAY_LINKLEFT(head, __right, field); \
|
||||
} else if (__comp > 0) { \
|
||||
__tmp = SPLAY_RIGHT((head)->sph_root, field); \
|
||||
if (__tmp == NULL) \
|
||||
break; \
|
||||
if (__comp > 0) { \
|
||||
SPLAY_ROTATE_LEFT(head, __tmp, field); \
|
||||
if (SPLAY_RIGHT((head)->sph_root, field) == NULL) \
|
||||
break; \
|
||||
} \
|
||||
SPLAY_LINKRIGHT(head, __left, field); \
|
||||
} \
|
||||
} \
|
||||
SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \
|
||||
}
|
||||
|
||||
#define SPLAY_NEGINF -1
|
||||
#define SPLAY_INF 1
|
||||
|
||||
#define SPLAY_INSERT(name, x, y) name##_SPLAY_INSERT(x, y)
|
||||
#define SPLAY_REMOVE(name, x, y) name##_SPLAY_REMOVE(x, y)
|
||||
#define SPLAY_FIND(name, x, y) name##_SPLAY_FIND(x, y)
|
||||
#define SPLAY_NEXT(name, x, y) name##_SPLAY_NEXT(x, y)
|
||||
#define SPLAY_MIN(name, x) (SPLAY_EMPTY(x) ? NULL \
|
||||
: name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF))
|
||||
#define SPLAY_MAX(name, x) (SPLAY_EMPTY(x) ? NULL \
|
||||
: name##_SPLAY_MIN_MAX(x, SPLAY_INF))
|
||||
|
||||
#define SPLAY_FOREACH(x, name, head) \
|
||||
for ((x) = SPLAY_MIN(name, head); \
|
||||
(x) != NULL; \
|
||||
(x) = SPLAY_NEXT(name, head, x))
|
||||
|
||||
/* Macros that define a red-black tree */
|
||||
#define RB_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *rbh_root; /* root of the tree */ \
|
||||
}
|
||||
|
||||
#define RB_INITIALIZER(root) \
|
||||
{ NULL }
|
||||
|
||||
#define RB_INIT(root) do { \
|
||||
(root)->rbh_root = NULL; \
|
||||
} while (/*CONSTCOND*/ 0)
|
||||
|
||||
#define RB_BLACK 0
|
||||
#define RB_RED 1
|
||||
#define RB_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *rbe_left; /* left element */ \
|
||||
struct type *rbe_right; /* right element */ \
|
||||
struct type *rbe_parent; /* parent element */ \
|
||||
int rbe_color; /* node color */ \
|
||||
}
|
||||
|
||||
#define RB_LEFT(elm, field) (elm)->field.rbe_left
|
||||
#define RB_RIGHT(elm, field) (elm)->field.rbe_right
|
||||
#define RB_PARENT(elm, field) (elm)->field.rbe_parent
|
||||
#define RB_COLOR(elm, field) (elm)->field.rbe_color
|
||||
#define RB_ROOT(head) (head)->rbh_root
|
||||
#define RB_EMPTY(head) (RB_ROOT(head) == NULL)
|
||||
|
||||
#define RB_SET(elm, parent, field) do { \
|
||||
RB_PARENT(elm, field) = parent; \
|
||||
RB_LEFT(elm, field) = RB_RIGHT(elm, field) = NULL; \
|
||||
RB_COLOR(elm, field) = RB_RED; \
|
||||
} while (/*CONSTCOND*/ 0)
|
||||
|
||||
#define RB_SET_BLACKRED(black, red, field) do { \
|
||||
RB_COLOR(black, field) = RB_BLACK; \
|
||||
RB_COLOR(red, field) = RB_RED; \
|
||||
} while (/*CONSTCOND*/ 0)
|
||||
|
||||
#ifndef RB_AUGMENT
|
||||
#define RB_AUGMENT(x) do {} while (0)
|
||||
#endif
|
||||
|
||||
#define RB_ROTATE_LEFT(head, elm, tmp, field) do { \
|
||||
(tmp) = RB_RIGHT(elm, field); \
|
||||
if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field)) != NULL) { \
|
||||
RB_PARENT(RB_LEFT(tmp, field), field) = (elm); \
|
||||
} \
|
||||
RB_AUGMENT(elm); \
|
||||
if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field)) != NULL) { \
|
||||
if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \
|
||||
RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \
|
||||
else \
|
||||
RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \
|
||||
} else \
|
||||
(head)->rbh_root = (tmp); \
|
||||
RB_LEFT(tmp, field) = (elm); \
|
||||
RB_PARENT(elm, field) = (tmp); \
|
||||
RB_AUGMENT(tmp); \
|
||||
if ((RB_PARENT(tmp, field))) \
|
||||
RB_AUGMENT(RB_PARENT(tmp, field)); \
|
||||
} while (/*CONSTCOND*/ 0)
|
||||
|
||||
#define RB_ROTATE_RIGHT(head, elm, tmp, field) do { \
|
||||
(tmp) = RB_LEFT(elm, field); \
|
||||
if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field)) != NULL) { \
|
||||
RB_PARENT(RB_RIGHT(tmp, field), field) = (elm); \
|
||||
} \
|
||||
RB_AUGMENT(elm); \
|
||||
if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field)) != NULL) { \
|
||||
if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \
|
||||
RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \
|
||||
else \
|
||||
RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \
|
||||
} else \
|
||||
(head)->rbh_root = (tmp); \
|
||||
RB_RIGHT(tmp, field) = (elm); \
|
||||
RB_PARENT(elm, field) = (tmp); \
|
||||
RB_AUGMENT(tmp); \
|
||||
if ((RB_PARENT(tmp, field))) \
|
||||
RB_AUGMENT(RB_PARENT(tmp, field)); \
|
||||
} while (/*CONSTCOND*/ 0)
|
||||
|
||||
/* Generates prototypes and inline functions */
|
||||
#define RB_PROTOTYPE(name, type, field, cmp) \
|
||||
RB_PROTOTYPE_INTERNAL(name, type, field, cmp,)
|
||||
#define RB_PROTOTYPE_STATIC(name, type, field, cmp) \
|
||||
RB_PROTOTYPE_INTERNAL(name, type, field, cmp, UV__UNUSED static)
|
||||
#define RB_PROTOTYPE_INTERNAL(name, type, field, cmp, attr) \
|
||||
attr void name##_RB_INSERT_COLOR(struct name *, struct type *); \
|
||||
attr void name##_RB_REMOVE_COLOR(struct name *, struct type *, struct type *);\
|
||||
attr struct type *name##_RB_REMOVE(struct name *, struct type *); \
|
||||
attr struct type *name##_RB_INSERT(struct name *, struct type *); \
|
||||
attr struct type *name##_RB_FIND(struct name *, struct type *); \
|
||||
attr struct type *name##_RB_NFIND(struct name *, struct type *); \
|
||||
attr struct type *name##_RB_NEXT(struct type *); \
|
||||
attr struct type *name##_RB_PREV(struct type *); \
|
||||
attr struct type *name##_RB_MINMAX(struct name *, int); \
|
||||
\
|
||||
|
||||
/* Main rb operation.
|
||||
* Moves node close to the key of elm to top
|
||||
*/
|
||||
#define RB_GENERATE(name, type, field, cmp) \
|
||||
RB_GENERATE_INTERNAL(name, type, field, cmp,)
|
||||
#define RB_GENERATE_STATIC(name, type, field, cmp) \
|
||||
RB_GENERATE_INTERNAL(name, type, field, cmp, UV__UNUSED static)
|
||||
#define RB_GENERATE_INTERNAL(name, type, field, cmp, attr) \
|
||||
attr void \
|
||||
name##_RB_INSERT_COLOR(struct name *head, struct type *elm) \
|
||||
{ \
|
||||
struct type *parent, *gparent, *tmp; \
|
||||
while ((parent = RB_PARENT(elm, field)) != NULL && \
|
||||
RB_COLOR(parent, field) == RB_RED) { \
|
||||
gparent = RB_PARENT(parent, field); \
|
||||
if (parent == RB_LEFT(gparent, field)) { \
|
||||
tmp = RB_RIGHT(gparent, field); \
|
||||
if (tmp && RB_COLOR(tmp, field) == RB_RED) { \
|
||||
RB_COLOR(tmp, field) = RB_BLACK; \
|
||||
RB_SET_BLACKRED(parent, gparent, field); \
|
||||
elm = gparent; \
|
||||
continue; \
|
||||
} \
|
||||
if (RB_RIGHT(parent, field) == elm) { \
|
||||
RB_ROTATE_LEFT(head, parent, tmp, field); \
|
||||
tmp = parent; \
|
||||
parent = elm; \
|
||||
elm = tmp; \
|
||||
} \
|
||||
RB_SET_BLACKRED(parent, gparent, field); \
|
||||
RB_ROTATE_RIGHT(head, gparent, tmp, field); \
|
||||
} else { \
|
||||
tmp = RB_LEFT(gparent, field); \
|
||||
if (tmp && RB_COLOR(tmp, field) == RB_RED) { \
|
||||
RB_COLOR(tmp, field) = RB_BLACK; \
|
||||
RB_SET_BLACKRED(parent, gparent, field); \
|
||||
elm = gparent; \
|
||||
continue; \
|
||||
} \
|
||||
if (RB_LEFT(parent, field) == elm) { \
|
||||
RB_ROTATE_RIGHT(head, parent, tmp, field); \
|
||||
tmp = parent; \
|
||||
parent = elm; \
|
||||
elm = tmp; \
|
||||
} \
|
||||
RB_SET_BLACKRED(parent, gparent, field); \
|
||||
RB_ROTATE_LEFT(head, gparent, tmp, field); \
|
||||
} \
|
||||
} \
|
||||
RB_COLOR(head->rbh_root, field) = RB_BLACK; \
|
||||
} \
|
||||
\
|
||||
attr void \
|
||||
name##_RB_REMOVE_COLOR(struct name *head, struct type *parent, \
|
||||
struct type *elm) \
|
||||
{ \
|
||||
struct type *tmp; \
|
||||
while ((elm == NULL || RB_COLOR(elm, field) == RB_BLACK) && \
|
||||
elm != RB_ROOT(head)) { \
|
||||
if (RB_LEFT(parent, field) == elm) { \
|
||||
tmp = RB_RIGHT(parent, field); \
|
||||
if (RB_COLOR(tmp, field) == RB_RED) { \
|
||||
RB_SET_BLACKRED(tmp, parent, field); \
|
||||
RB_ROTATE_LEFT(head, parent, tmp, field); \
|
||||
tmp = RB_RIGHT(parent, field); \
|
||||
} \
|
||||
if ((RB_LEFT(tmp, field) == NULL || \
|
||||
RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) && \
|
||||
(RB_RIGHT(tmp, field) == NULL || \
|
||||
RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) { \
|
||||
RB_COLOR(tmp, field) = RB_RED; \
|
||||
elm = parent; \
|
||||
parent = RB_PARENT(elm, field); \
|
||||
} else { \
|
||||
if (RB_RIGHT(tmp, field) == NULL || \
|
||||
RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK) { \
|
||||
struct type *oleft; \
|
||||
if ((oleft = RB_LEFT(tmp, field)) \
|
||||
!= NULL) \
|
||||
RB_COLOR(oleft, field) = RB_BLACK; \
|
||||
RB_COLOR(tmp, field) = RB_RED; \
|
||||
RB_ROTATE_RIGHT(head, tmp, oleft, field); \
|
||||
tmp = RB_RIGHT(parent, field); \
|
||||
} \
|
||||
RB_COLOR(tmp, field) = RB_COLOR(parent, field); \
|
||||
RB_COLOR(parent, field) = RB_BLACK; \
|
||||
if (RB_RIGHT(tmp, field)) \
|
||||
RB_COLOR(RB_RIGHT(tmp, field), field) = RB_BLACK; \
|
||||
RB_ROTATE_LEFT(head, parent, tmp, field); \
|
||||
elm = RB_ROOT(head); \
|
||||
break; \
|
||||
} \
|
||||
} else { \
|
||||
tmp = RB_LEFT(parent, field); \
|
||||
if (RB_COLOR(tmp, field) == RB_RED) { \
|
||||
RB_SET_BLACKRED(tmp, parent, field); \
|
||||
RB_ROTATE_RIGHT(head, parent, tmp, field); \
|
||||
tmp = RB_LEFT(parent, field); \
|
||||
} \
|
||||
if ((RB_LEFT(tmp, field) == NULL || \
|
||||
RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) && \
|
||||
(RB_RIGHT(tmp, field) == NULL || \
|
||||
RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) { \
|
||||
RB_COLOR(tmp, field) = RB_RED; \
|
||||
elm = parent; \
|
||||
parent = RB_PARENT(elm, field); \
|
||||
} else { \
|
||||
if (RB_LEFT(tmp, field) == NULL || \
|
||||
RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) { \
|
||||
struct type *oright; \
|
||||
if ((oright = RB_RIGHT(tmp, field)) \
|
||||
!= NULL) \
|
||||
RB_COLOR(oright, field) = RB_BLACK; \
|
||||
RB_COLOR(tmp, field) = RB_RED; \
|
||||
RB_ROTATE_LEFT(head, tmp, oright, field); \
|
||||
tmp = RB_LEFT(parent, field); \
|
||||
} \
|
||||
RB_COLOR(tmp, field) = RB_COLOR(parent, field); \
|
||||
RB_COLOR(parent, field) = RB_BLACK; \
|
||||
if (RB_LEFT(tmp, field)) \
|
||||
RB_COLOR(RB_LEFT(tmp, field), field) = RB_BLACK; \
|
||||
RB_ROTATE_RIGHT(head, parent, tmp, field); \
|
||||
elm = RB_ROOT(head); \
|
||||
break; \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
if (elm) \
|
||||
RB_COLOR(elm, field) = RB_BLACK; \
|
||||
} \
|
||||
\
|
||||
attr struct type * \
|
||||
name##_RB_REMOVE(struct name *head, struct type *elm) \
|
||||
{ \
|
||||
struct type *child, *parent, *old = elm; \
|
||||
int color; \
|
||||
if (RB_LEFT(elm, field) == NULL) \
|
||||
child = RB_RIGHT(elm, field); \
|
||||
else if (RB_RIGHT(elm, field) == NULL) \
|
||||
child = RB_LEFT(elm, field); \
|
||||
else { \
|
||||
struct type *left; \
|
||||
elm = RB_RIGHT(elm, field); \
|
||||
while ((left = RB_LEFT(elm, field)) != NULL) \
|
||||
elm = left; \
|
||||
child = RB_RIGHT(elm, field); \
|
||||
parent = RB_PARENT(elm, field); \
|
||||
color = RB_COLOR(elm, field); \
|
||||
if (child) \
|
||||
RB_PARENT(child, field) = parent; \
|
||||
if (parent) { \
|
||||
if (RB_LEFT(parent, field) == elm) \
|
||||
RB_LEFT(parent, field) = child; \
|
||||
else \
|
||||
RB_RIGHT(parent, field) = child; \
|
||||
RB_AUGMENT(parent); \
|
||||
} else \
|
||||
RB_ROOT(head) = child; \
|
||||
if (RB_PARENT(elm, field) == old) \
|
||||
parent = elm; \
|
||||
(elm)->field = (old)->field; \
|
||||
if (RB_PARENT(old, field)) { \
|
||||
if (RB_LEFT(RB_PARENT(old, field), field) == old) \
|
||||
RB_LEFT(RB_PARENT(old, field), field) = elm; \
|
||||
else \
|
||||
RB_RIGHT(RB_PARENT(old, field), field) = elm; \
|
||||
RB_AUGMENT(RB_PARENT(old, field)); \
|
||||
} else \
|
||||
RB_ROOT(head) = elm; \
|
||||
RB_PARENT(RB_LEFT(old, field), field) = elm; \
|
||||
if (RB_RIGHT(old, field)) \
|
||||
RB_PARENT(RB_RIGHT(old, field), field) = elm; \
|
||||
if (parent) { \
|
||||
left = parent; \
|
||||
do { \
|
||||
RB_AUGMENT(left); \
|
||||
} while ((left = RB_PARENT(left, field)) != NULL); \
|
||||
} \
|
||||
goto color; \
|
||||
} \
|
||||
parent = RB_PARENT(elm, field); \
|
||||
color = RB_COLOR(elm, field); \
|
||||
if (child) \
|
||||
RB_PARENT(child, field) = parent; \
|
||||
if (parent) { \
|
||||
if (RB_LEFT(parent, field) == elm) \
|
||||
RB_LEFT(parent, field) = child; \
|
||||
else \
|
||||
RB_RIGHT(parent, field) = child; \
|
||||
RB_AUGMENT(parent); \
|
||||
} else \
|
||||
RB_ROOT(head) = child; \
|
||||
color: \
|
||||
if (color == RB_BLACK) \
|
||||
name##_RB_REMOVE_COLOR(head, parent, child); \
|
||||
return (old); \
|
||||
} \
|
||||
\
|
||||
/* Inserts a node into the RB tree */ \
|
||||
attr struct type * \
|
||||
name##_RB_INSERT(struct name *head, struct type *elm) \
|
||||
{ \
|
||||
struct type *tmp; \
|
||||
struct type *parent = NULL; \
|
||||
int comp = 0; \
|
||||
tmp = RB_ROOT(head); \
|
||||
while (tmp) { \
|
||||
parent = tmp; \
|
||||
comp = (cmp)(elm, parent); \
|
||||
if (comp < 0) \
|
||||
tmp = RB_LEFT(tmp, field); \
|
||||
else if (comp > 0) \
|
||||
tmp = RB_RIGHT(tmp, field); \
|
||||
else \
|
||||
return (tmp); \
|
||||
} \
|
||||
RB_SET(elm, parent, field); \
|
||||
if (parent != NULL) { \
|
||||
if (comp < 0) \
|
||||
RB_LEFT(parent, field) = elm; \
|
||||
else \
|
||||
RB_RIGHT(parent, field) = elm; \
|
||||
RB_AUGMENT(parent); \
|
||||
} else \
|
||||
RB_ROOT(head) = elm; \
|
||||
name##_RB_INSERT_COLOR(head, elm); \
|
||||
return (NULL); \
|
||||
} \
|
||||
\
|
||||
/* Finds the node with the same key as elm */ \
|
||||
attr struct type * \
|
||||
name##_RB_FIND(struct name *head, struct type *elm) \
|
||||
{ \
|
||||
struct type *tmp = RB_ROOT(head); \
|
||||
int comp; \
|
||||
while (tmp) { \
|
||||
comp = cmp(elm, tmp); \
|
||||
if (comp < 0) \
|
||||
tmp = RB_LEFT(tmp, field); \
|
||||
else if (comp > 0) \
|
||||
tmp = RB_RIGHT(tmp, field); \
|
||||
else \
|
||||
return (tmp); \
|
||||
} \
|
||||
return (NULL); \
|
||||
} \
|
||||
\
|
||||
/* Finds the first node greater than or equal to the search key */ \
|
||||
attr struct type * \
|
||||
name##_RB_NFIND(struct name *head, struct type *elm) \
|
||||
{ \
|
||||
struct type *tmp = RB_ROOT(head); \
|
||||
struct type *res = NULL; \
|
||||
int comp; \
|
||||
while (tmp) { \
|
||||
comp = cmp(elm, tmp); \
|
||||
if (comp < 0) { \
|
||||
res = tmp; \
|
||||
tmp = RB_LEFT(tmp, field); \
|
||||
} \
|
||||
else if (comp > 0) \
|
||||
tmp = RB_RIGHT(tmp, field); \
|
||||
else \
|
||||
return (tmp); \
|
||||
} \
|
||||
return (res); \
|
||||
} \
|
||||
\
|
||||
/* ARGSUSED */ \
|
||||
attr struct type * \
|
||||
name##_RB_NEXT(struct type *elm) \
|
||||
{ \
|
||||
if (RB_RIGHT(elm, field)) { \
|
||||
elm = RB_RIGHT(elm, field); \
|
||||
while (RB_LEFT(elm, field)) \
|
||||
elm = RB_LEFT(elm, field); \
|
||||
} else { \
|
||||
if (RB_PARENT(elm, field) && \
|
||||
(elm == RB_LEFT(RB_PARENT(elm, field), field))) \
|
||||
elm = RB_PARENT(elm, field); \
|
||||
else { \
|
||||
while (RB_PARENT(elm, field) && \
|
||||
(elm == RB_RIGHT(RB_PARENT(elm, field), field))) \
|
||||
elm = RB_PARENT(elm, field); \
|
||||
elm = RB_PARENT(elm, field); \
|
||||
} \
|
||||
} \
|
||||
return (elm); \
|
||||
} \
|
||||
\
|
||||
/* ARGSUSED */ \
|
||||
attr struct type * \
|
||||
name##_RB_PREV(struct type *elm) \
|
||||
{ \
|
||||
if (RB_LEFT(elm, field)) { \
|
||||
elm = RB_LEFT(elm, field); \
|
||||
while (RB_RIGHT(elm, field)) \
|
||||
elm = RB_RIGHT(elm, field); \
|
||||
} else { \
|
||||
if (RB_PARENT(elm, field) && \
|
||||
(elm == RB_RIGHT(RB_PARENT(elm, field), field))) \
|
||||
elm = RB_PARENT(elm, field); \
|
||||
else { \
|
||||
while (RB_PARENT(elm, field) && \
|
||||
(elm == RB_LEFT(RB_PARENT(elm, field), field))) \
|
||||
elm = RB_PARENT(elm, field); \
|
||||
elm = RB_PARENT(elm, field); \
|
||||
} \
|
||||
} \
|
||||
return (elm); \
|
||||
} \
|
||||
\
|
||||
attr struct type * \
|
||||
name##_RB_MINMAX(struct name *head, int val) \
|
||||
{ \
|
||||
struct type *tmp = RB_ROOT(head); \
|
||||
struct type *parent = NULL; \
|
||||
while (tmp) { \
|
||||
parent = tmp; \
|
||||
if (val < 0) \
|
||||
tmp = RB_LEFT(tmp, field); \
|
||||
else \
|
||||
tmp = RB_RIGHT(tmp, field); \
|
||||
} \
|
||||
return (parent); \
|
||||
}
|
||||
|
||||
#define RB_NEGINF -1
|
||||
#define RB_INF 1
|
||||
|
||||
#define RB_INSERT(name, x, y) name##_RB_INSERT(x, y)
|
||||
#define RB_REMOVE(name, x, y) name##_RB_REMOVE(x, y)
|
||||
#define RB_FIND(name, x, y) name##_RB_FIND(x, y)
|
||||
#define RB_NFIND(name, x, y) name##_RB_NFIND(x, y)
|
||||
#define RB_NEXT(name, x, y) name##_RB_NEXT(y)
|
||||
#define RB_PREV(name, x, y) name##_RB_PREV(y)
|
||||
#define RB_MIN(name, x) name##_RB_MINMAX(x, RB_NEGINF)
|
||||
#define RB_MAX(name, x) name##_RB_MINMAX(x, RB_INF)
|
||||
|
||||
#define RB_FOREACH(x, name, head) \
|
||||
for ((x) = RB_MIN(name, head); \
|
||||
(x) != NULL; \
|
||||
(x) = name##_RB_NEXT(x))
|
||||
|
||||
#define RB_FOREACH_FROM(x, name, y) \
|
||||
for ((x) = (y); \
|
||||
((x) != NULL) && ((y) = name##_RB_NEXT(x), (x) != NULL); \
|
||||
(x) = (y))
|
||||
|
||||
#define RB_FOREACH_SAFE(x, name, head, y) \
|
||||
for ((x) = RB_MIN(name, head); \
|
||||
((x) != NULL) && ((y) = name##_RB_NEXT(x), (x) != NULL); \
|
||||
(x) = (y))
|
||||
|
||||
#define RB_FOREACH_REVERSE(x, name, head) \
|
||||
for ((x) = RB_MAX(name, head); \
|
||||
(x) != NULL; \
|
||||
(x) = name##_RB_PREV(x))
|
||||
|
||||
#define RB_FOREACH_REVERSE_FROM(x, name, y) \
|
||||
for ((x) = (y); \
|
||||
((x) != NULL) && ((y) = name##_RB_PREV(x), (x) != NULL); \
|
||||
(x) = (y))
|
||||
|
||||
#define RB_FOREACH_REVERSE_SAFE(x, name, head, y) \
|
||||
for ((x) = RB_MAX(name, head); \
|
||||
((x) != NULL) && ((y) = name##_RB_PREV(x), (x) != NULL); \
|
||||
(x) = (y))
|
||||
|
||||
#endif /* UV_TREE_H_ */
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,703 @@
|
||||
/*
|
||||
Copyright (c) 2013, Kenneth MacKay
|
||||
Copyright (c) 2014, Emergya (Cloud4all, FP7/2007-2013 grant agreement #289016)
|
||||
All rights reserved.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
#include "android-ifaddrs.h"
|
||||
#include "uv-common.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/socket.h>
|
||||
#include <net/if_arp.h>
|
||||
#include <netinet/in.h>
|
||||
#include <linux/netlink.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
|
||||
typedef struct NetlinkList
|
||||
{
|
||||
struct NetlinkList *m_next;
|
||||
struct nlmsghdr *m_data;
|
||||
unsigned int m_size;
|
||||
} NetlinkList;
|
||||
|
||||
static int netlink_socket(void)
|
||||
{
|
||||
struct sockaddr_nl l_addr;
|
||||
|
||||
int l_socket = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
|
||||
if(l_socket < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(&l_addr, 0, sizeof(l_addr));
|
||||
l_addr.nl_family = AF_NETLINK;
|
||||
if(bind(l_socket, (struct sockaddr *)&l_addr, sizeof(l_addr)) < 0)
|
||||
{
|
||||
close(l_socket);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return l_socket;
|
||||
}
|
||||
|
||||
static int netlink_send(int p_socket, int p_request)
|
||||
{
|
||||
char l_buffer[NLMSG_ALIGN(sizeof(struct nlmsghdr)) + NLMSG_ALIGN(sizeof(struct rtgenmsg))];
|
||||
|
||||
struct nlmsghdr *l_hdr;
|
||||
struct rtgenmsg *l_msg;
|
||||
struct sockaddr_nl l_addr;
|
||||
|
||||
memset(l_buffer, 0, sizeof(l_buffer));
|
||||
|
||||
l_hdr = (struct nlmsghdr *)l_buffer;
|
||||
l_msg = (struct rtgenmsg *)NLMSG_DATA(l_hdr);
|
||||
|
||||
l_hdr->nlmsg_len = NLMSG_LENGTH(sizeof(*l_msg));
|
||||
l_hdr->nlmsg_type = p_request;
|
||||
l_hdr->nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST;
|
||||
l_hdr->nlmsg_pid = 0;
|
||||
l_hdr->nlmsg_seq = p_socket;
|
||||
l_msg->rtgen_family = AF_UNSPEC;
|
||||
|
||||
memset(&l_addr, 0, sizeof(l_addr));
|
||||
l_addr.nl_family = AF_NETLINK;
|
||||
return (sendto(p_socket, l_hdr, l_hdr->nlmsg_len, 0, (struct sockaddr *)&l_addr, sizeof(l_addr)));
|
||||
}
|
||||
|
||||
static int netlink_recv(int p_socket, void *p_buffer, size_t p_len)
|
||||
{
|
||||
struct sockaddr_nl l_addr;
|
||||
struct msghdr l_msg;
|
||||
|
||||
struct iovec l_iov;
|
||||
l_iov.iov_base = p_buffer;
|
||||
l_iov.iov_len = p_len;
|
||||
|
||||
for(;;)
|
||||
{
|
||||
int l_result;
|
||||
l_msg.msg_name = (void *)&l_addr;
|
||||
l_msg.msg_namelen = sizeof(l_addr);
|
||||
l_msg.msg_iov = &l_iov;
|
||||
l_msg.msg_iovlen = 1;
|
||||
l_msg.msg_control = NULL;
|
||||
l_msg.msg_controllen = 0;
|
||||
l_msg.msg_flags = 0;
|
||||
l_result = recvmsg(p_socket, &l_msg, 0);
|
||||
|
||||
if(l_result < 0)
|
||||
{
|
||||
if(errno == EINTR)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
return -2;
|
||||
}
|
||||
|
||||
/* Buffer was too small */
|
||||
if(l_msg.msg_flags & MSG_TRUNC)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
return l_result;
|
||||
}
|
||||
}
|
||||
|
||||
static struct nlmsghdr *getNetlinkResponse(int p_socket, int *p_size, int *p_done)
|
||||
{
|
||||
size_t l_size = 4096;
|
||||
void *l_buffer = NULL;
|
||||
|
||||
for(;;)
|
||||
{
|
||||
int l_read;
|
||||
|
||||
uv__free(l_buffer);
|
||||
l_buffer = uv__malloc(l_size);
|
||||
if (l_buffer == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
l_read = netlink_recv(p_socket, l_buffer, l_size);
|
||||
*p_size = l_read;
|
||||
if(l_read == -2)
|
||||
{
|
||||
uv__free(l_buffer);
|
||||
return NULL;
|
||||
}
|
||||
if(l_read >= 0)
|
||||
{
|
||||
pid_t l_pid = getpid();
|
||||
struct nlmsghdr *l_hdr;
|
||||
for(l_hdr = (struct nlmsghdr *)l_buffer; NLMSG_OK(l_hdr, (unsigned int)l_read); l_hdr = (struct nlmsghdr *)NLMSG_NEXT(l_hdr, l_read))
|
||||
{
|
||||
if((pid_t)l_hdr->nlmsg_pid != l_pid || (int)l_hdr->nlmsg_seq != p_socket)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if(l_hdr->nlmsg_type == NLMSG_DONE)
|
||||
{
|
||||
*p_done = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
if(l_hdr->nlmsg_type == NLMSG_ERROR)
|
||||
{
|
||||
uv__free(l_buffer);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
return l_buffer;
|
||||
}
|
||||
|
||||
l_size *= 2;
|
||||
}
|
||||
}
|
||||
|
||||
static NetlinkList *newListItem(struct nlmsghdr *p_data, unsigned int p_size)
|
||||
{
|
||||
NetlinkList *l_item = uv__malloc(sizeof(NetlinkList));
|
||||
if (l_item == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
l_item->m_next = NULL;
|
||||
l_item->m_data = p_data;
|
||||
l_item->m_size = p_size;
|
||||
return l_item;
|
||||
}
|
||||
|
||||
static void freeResultList(NetlinkList *p_list)
|
||||
{
|
||||
NetlinkList *l_cur;
|
||||
while(p_list)
|
||||
{
|
||||
l_cur = p_list;
|
||||
p_list = p_list->m_next;
|
||||
uv__free(l_cur->m_data);
|
||||
uv__free(l_cur);
|
||||
}
|
||||
}
|
||||
|
||||
static NetlinkList *getResultList(int p_socket, int p_request)
|
||||
{
|
||||
int l_size;
|
||||
int l_done;
|
||||
NetlinkList *l_list;
|
||||
NetlinkList *l_end;
|
||||
|
||||
if(netlink_send(p_socket, p_request) < 0)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
l_list = NULL;
|
||||
l_end = NULL;
|
||||
|
||||
l_done = 0;
|
||||
while(!l_done)
|
||||
{
|
||||
NetlinkList *l_item;
|
||||
|
||||
struct nlmsghdr *l_hdr = getNetlinkResponse(p_socket, &l_size, &l_done);
|
||||
/* Error */
|
||||
if(!l_hdr)
|
||||
{
|
||||
freeResultList(l_list);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
l_item = newListItem(l_hdr, l_size);
|
||||
if (!l_item)
|
||||
{
|
||||
freeResultList(l_list);
|
||||
return NULL;
|
||||
}
|
||||
if(!l_list)
|
||||
{
|
||||
l_list = l_item;
|
||||
}
|
||||
else
|
||||
{
|
||||
l_end->m_next = l_item;
|
||||
}
|
||||
l_end = l_item;
|
||||
}
|
||||
return l_list;
|
||||
}
|
||||
|
||||
static size_t maxSize(size_t a, size_t b)
|
||||
{
|
||||
return (a > b ? a : b);
|
||||
}
|
||||
|
||||
static size_t calcAddrLen(sa_family_t p_family, int p_dataSize)
|
||||
{
|
||||
switch(p_family)
|
||||
{
|
||||
case AF_INET:
|
||||
return sizeof(struct sockaddr_in);
|
||||
case AF_INET6:
|
||||
return sizeof(struct sockaddr_in6);
|
||||
case AF_PACKET:
|
||||
return maxSize(sizeof(struct sockaddr_ll), offsetof(struct sockaddr_ll, sll_addr) + p_dataSize);
|
||||
default:
|
||||
return maxSize(sizeof(struct sockaddr), offsetof(struct sockaddr, sa_data) + p_dataSize);
|
||||
}
|
||||
}
|
||||
|
||||
static void makeSockaddr(sa_family_t p_family, struct sockaddr *p_dest, void *p_data, size_t p_size)
|
||||
{
|
||||
switch(p_family)
|
||||
{
|
||||
case AF_INET:
|
||||
memcpy(&((struct sockaddr_in*)p_dest)->sin_addr, p_data, p_size);
|
||||
break;
|
||||
case AF_INET6:
|
||||
memcpy(&((struct sockaddr_in6*)p_dest)->sin6_addr, p_data, p_size);
|
||||
break;
|
||||
case AF_PACKET:
|
||||
memcpy(((struct sockaddr_ll*)p_dest)->sll_addr, p_data, p_size);
|
||||
((struct sockaddr_ll*)p_dest)->sll_halen = p_size;
|
||||
break;
|
||||
default:
|
||||
memcpy(p_dest->sa_data, p_data, p_size);
|
||||
break;
|
||||
}
|
||||
p_dest->sa_family = p_family;
|
||||
}
|
||||
|
||||
static void addToEnd(struct ifaddrs **p_resultList, struct ifaddrs *p_entry)
|
||||
{
|
||||
if(!*p_resultList)
|
||||
{
|
||||
*p_resultList = p_entry;
|
||||
}
|
||||
else
|
||||
{
|
||||
struct ifaddrs *l_cur = *p_resultList;
|
||||
while(l_cur->ifa_next)
|
||||
{
|
||||
l_cur = l_cur->ifa_next;
|
||||
}
|
||||
l_cur->ifa_next = p_entry;
|
||||
}
|
||||
}
|
||||
|
||||
static int interpretLink(struct nlmsghdr *p_hdr, struct ifaddrs **p_resultList)
|
||||
{
|
||||
struct ifaddrs *l_entry;
|
||||
|
||||
char *l_index;
|
||||
char *l_name;
|
||||
char *l_addr;
|
||||
char *l_data;
|
||||
|
||||
struct ifinfomsg *l_info = (struct ifinfomsg *)NLMSG_DATA(p_hdr);
|
||||
|
||||
size_t l_nameSize = 0;
|
||||
size_t l_addrSize = 0;
|
||||
size_t l_dataSize = 0;
|
||||
|
||||
size_t l_rtaSize = NLMSG_PAYLOAD(p_hdr, sizeof(struct ifinfomsg));
|
||||
struct rtattr *l_rta;
|
||||
for(l_rta = IFLA_RTA(l_info); RTA_OK(l_rta, l_rtaSize); l_rta = RTA_NEXT(l_rta, l_rtaSize))
|
||||
{
|
||||
size_t l_rtaDataSize = RTA_PAYLOAD(l_rta);
|
||||
switch(l_rta->rta_type)
|
||||
{
|
||||
case IFLA_ADDRESS:
|
||||
case IFLA_BROADCAST:
|
||||
l_addrSize += NLMSG_ALIGN(calcAddrLen(AF_PACKET, l_rtaDataSize));
|
||||
break;
|
||||
case IFLA_IFNAME:
|
||||
l_nameSize += NLMSG_ALIGN(l_rtaSize + 1);
|
||||
break;
|
||||
case IFLA_STATS:
|
||||
l_dataSize += NLMSG_ALIGN(l_rtaSize);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
l_entry = uv__malloc(sizeof(struct ifaddrs) + sizeof(int) + l_nameSize + l_addrSize + l_dataSize);
|
||||
if (l_entry == NULL)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
memset(l_entry, 0, sizeof(struct ifaddrs));
|
||||
l_entry->ifa_name = "";
|
||||
|
||||
l_index = ((char *)l_entry) + sizeof(struct ifaddrs);
|
||||
l_name = l_index + sizeof(int);
|
||||
l_addr = l_name + l_nameSize;
|
||||
l_data = l_addr + l_addrSize;
|
||||
|
||||
/* Save the interface index so we can look it up when handling the
|
||||
* addresses.
|
||||
*/
|
||||
memcpy(l_index, &l_info->ifi_index, sizeof(int));
|
||||
|
||||
l_entry->ifa_flags = l_info->ifi_flags;
|
||||
|
||||
l_rtaSize = NLMSG_PAYLOAD(p_hdr, sizeof(struct ifinfomsg));
|
||||
for(l_rta = IFLA_RTA(l_info); RTA_OK(l_rta, l_rtaSize); l_rta = RTA_NEXT(l_rta, l_rtaSize))
|
||||
{
|
||||
void *l_rtaData = RTA_DATA(l_rta);
|
||||
size_t l_rtaDataSize = RTA_PAYLOAD(l_rta);
|
||||
switch(l_rta->rta_type)
|
||||
{
|
||||
case IFLA_ADDRESS:
|
||||
case IFLA_BROADCAST:
|
||||
{
|
||||
size_t l_addrLen = calcAddrLen(AF_PACKET, l_rtaDataSize);
|
||||
makeSockaddr(AF_PACKET, (struct sockaddr *)l_addr, l_rtaData, l_rtaDataSize);
|
||||
((struct sockaddr_ll *)l_addr)->sll_ifindex = l_info->ifi_index;
|
||||
((struct sockaddr_ll *)l_addr)->sll_hatype = l_info->ifi_type;
|
||||
if(l_rta->rta_type == IFLA_ADDRESS)
|
||||
{
|
||||
l_entry->ifa_addr = (struct sockaddr *)l_addr;
|
||||
}
|
||||
else
|
||||
{
|
||||
l_entry->ifa_broadaddr = (struct sockaddr *)l_addr;
|
||||
}
|
||||
l_addr += NLMSG_ALIGN(l_addrLen);
|
||||
break;
|
||||
}
|
||||
case IFLA_IFNAME:
|
||||
strncpy(l_name, l_rtaData, l_rtaDataSize);
|
||||
l_name[l_rtaDataSize] = '\0';
|
||||
l_entry->ifa_name = l_name;
|
||||
break;
|
||||
case IFLA_STATS:
|
||||
memcpy(l_data, l_rtaData, l_rtaDataSize);
|
||||
l_entry->ifa_data = l_data;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
addToEnd(p_resultList, l_entry);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct ifaddrs *findInterface(int p_index, struct ifaddrs **p_links, int p_numLinks)
|
||||
{
|
||||
int l_num = 0;
|
||||
struct ifaddrs *l_cur = *p_links;
|
||||
while(l_cur && l_num < p_numLinks)
|
||||
{
|
||||
char *l_indexPtr = ((char *)l_cur) + sizeof(struct ifaddrs);
|
||||
int l_index;
|
||||
memcpy(&l_index, l_indexPtr, sizeof(int));
|
||||
if(l_index == p_index)
|
||||
{
|
||||
return l_cur;
|
||||
}
|
||||
|
||||
l_cur = l_cur->ifa_next;
|
||||
++l_num;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int interpretAddr(struct nlmsghdr *p_hdr, struct ifaddrs **p_resultList, int p_numLinks)
|
||||
{
|
||||
struct ifaddrmsg *l_info = (struct ifaddrmsg *)NLMSG_DATA(p_hdr);
|
||||
struct ifaddrs *l_interface = findInterface(l_info->ifa_index, p_resultList, p_numLinks);
|
||||
|
||||
size_t l_nameSize = 0;
|
||||
size_t l_addrSize = 0;
|
||||
|
||||
int l_addedNetmask = 0;
|
||||
|
||||
size_t l_rtaSize = NLMSG_PAYLOAD(p_hdr, sizeof(struct ifaddrmsg));
|
||||
struct rtattr *l_rta;
|
||||
struct ifaddrs *l_entry;
|
||||
|
||||
char *l_name;
|
||||
char *l_addr;
|
||||
|
||||
for(l_rta = IFLA_RTA(l_info); RTA_OK(l_rta, l_rtaSize); l_rta = RTA_NEXT(l_rta, l_rtaSize))
|
||||
{
|
||||
size_t l_rtaDataSize = RTA_PAYLOAD(l_rta);
|
||||
if(l_info->ifa_family == AF_PACKET)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
switch(l_rta->rta_type)
|
||||
{
|
||||
case IFA_ADDRESS:
|
||||
case IFA_LOCAL:
|
||||
if((l_info->ifa_family == AF_INET || l_info->ifa_family == AF_INET6) && !l_addedNetmask)
|
||||
{
|
||||
/* Make room for netmask */
|
||||
l_addrSize += NLMSG_ALIGN(calcAddrLen(l_info->ifa_family, l_rtaDataSize));
|
||||
l_addedNetmask = 1;
|
||||
}
|
||||
case IFA_BROADCAST:
|
||||
l_addrSize += NLMSG_ALIGN(calcAddrLen(l_info->ifa_family, l_rtaDataSize));
|
||||
break;
|
||||
case IFA_LABEL:
|
||||
l_nameSize += NLMSG_ALIGN(l_rtaSize + 1);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
l_entry = uv__malloc(sizeof(struct ifaddrs) + l_nameSize + l_addrSize);
|
||||
if (l_entry == NULL)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
memset(l_entry, 0, sizeof(struct ifaddrs));
|
||||
l_entry->ifa_name = (l_interface ? l_interface->ifa_name : "");
|
||||
|
||||
l_name = ((char *)l_entry) + sizeof(struct ifaddrs);
|
||||
l_addr = l_name + l_nameSize;
|
||||
|
||||
l_entry->ifa_flags = l_info->ifa_flags;
|
||||
if(l_interface)
|
||||
{
|
||||
l_entry->ifa_flags |= l_interface->ifa_flags;
|
||||
}
|
||||
|
||||
l_rtaSize = NLMSG_PAYLOAD(p_hdr, sizeof(struct ifaddrmsg));
|
||||
for(l_rta = IFLA_RTA(l_info); RTA_OK(l_rta, l_rtaSize); l_rta = RTA_NEXT(l_rta, l_rtaSize))
|
||||
{
|
||||
void *l_rtaData = RTA_DATA(l_rta);
|
||||
size_t l_rtaDataSize = RTA_PAYLOAD(l_rta);
|
||||
switch(l_rta->rta_type)
|
||||
{
|
||||
case IFA_ADDRESS:
|
||||
case IFA_BROADCAST:
|
||||
case IFA_LOCAL:
|
||||
{
|
||||
size_t l_addrLen = calcAddrLen(l_info->ifa_family, l_rtaDataSize);
|
||||
makeSockaddr(l_info->ifa_family, (struct sockaddr *)l_addr, l_rtaData, l_rtaDataSize);
|
||||
if(l_info->ifa_family == AF_INET6)
|
||||
{
|
||||
if(IN6_IS_ADDR_LINKLOCAL((struct in6_addr *)l_rtaData) || IN6_IS_ADDR_MC_LINKLOCAL((struct in6_addr *)l_rtaData))
|
||||
{
|
||||
((struct sockaddr_in6 *)l_addr)->sin6_scope_id = l_info->ifa_index;
|
||||
}
|
||||
}
|
||||
|
||||
/* Apparently in a point-to-point network IFA_ADDRESS contains
|
||||
* the dest address and IFA_LOCAL contains the local address
|
||||
*/
|
||||
if(l_rta->rta_type == IFA_ADDRESS)
|
||||
{
|
||||
if(l_entry->ifa_addr)
|
||||
{
|
||||
l_entry->ifa_dstaddr = (struct sockaddr *)l_addr;
|
||||
}
|
||||
else
|
||||
{
|
||||
l_entry->ifa_addr = (struct sockaddr *)l_addr;
|
||||
}
|
||||
}
|
||||
else if(l_rta->rta_type == IFA_LOCAL)
|
||||
{
|
||||
if(l_entry->ifa_addr)
|
||||
{
|
||||
l_entry->ifa_dstaddr = l_entry->ifa_addr;
|
||||
}
|
||||
l_entry->ifa_addr = (struct sockaddr *)l_addr;
|
||||
}
|
||||
else
|
||||
{
|
||||
l_entry->ifa_broadaddr = (struct sockaddr *)l_addr;
|
||||
}
|
||||
l_addr += NLMSG_ALIGN(l_addrLen);
|
||||
break;
|
||||
}
|
||||
case IFA_LABEL:
|
||||
strncpy(l_name, l_rtaData, l_rtaDataSize);
|
||||
l_name[l_rtaDataSize] = '\0';
|
||||
l_entry->ifa_name = l_name;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(l_entry->ifa_addr && (l_entry->ifa_addr->sa_family == AF_INET || l_entry->ifa_addr->sa_family == AF_INET6))
|
||||
{
|
||||
unsigned l_maxPrefix = (l_entry->ifa_addr->sa_family == AF_INET ? 32 : 128);
|
||||
unsigned l_prefix = (l_info->ifa_prefixlen > l_maxPrefix ? l_maxPrefix : l_info->ifa_prefixlen);
|
||||
char l_mask[16] = {0};
|
||||
unsigned i;
|
||||
for(i=0; i<(l_prefix/8); ++i)
|
||||
{
|
||||
l_mask[i] = 0xff;
|
||||
}
|
||||
if(l_prefix % 8)
|
||||
{
|
||||
l_mask[i] = 0xff << (8 - (l_prefix % 8));
|
||||
}
|
||||
|
||||
makeSockaddr(l_entry->ifa_addr->sa_family, (struct sockaddr *)l_addr, l_mask, l_maxPrefix / 8);
|
||||
l_entry->ifa_netmask = (struct sockaddr *)l_addr;
|
||||
}
|
||||
|
||||
addToEnd(p_resultList, l_entry);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int interpretLinks(int p_socket, NetlinkList *p_netlinkList, struct ifaddrs **p_resultList)
|
||||
{
|
||||
|
||||
int l_numLinks = 0;
|
||||
pid_t l_pid = getpid();
|
||||
for(; p_netlinkList; p_netlinkList = p_netlinkList->m_next)
|
||||
{
|
||||
unsigned int l_nlsize = p_netlinkList->m_size;
|
||||
struct nlmsghdr *l_hdr;
|
||||
for(l_hdr = p_netlinkList->m_data; NLMSG_OK(l_hdr, l_nlsize); l_hdr = NLMSG_NEXT(l_hdr, l_nlsize))
|
||||
{
|
||||
if((pid_t)l_hdr->nlmsg_pid != l_pid || (int)l_hdr->nlmsg_seq != p_socket)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if(l_hdr->nlmsg_type == NLMSG_DONE)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if(l_hdr->nlmsg_type == RTM_NEWLINK)
|
||||
{
|
||||
if(interpretLink(l_hdr, p_resultList) == -1)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
++l_numLinks;
|
||||
}
|
||||
}
|
||||
}
|
||||
return l_numLinks;
|
||||
}
|
||||
|
||||
static int interpretAddrs(int p_socket, NetlinkList *p_netlinkList, struct ifaddrs **p_resultList, int p_numLinks)
|
||||
{
|
||||
pid_t l_pid = getpid();
|
||||
for(; p_netlinkList; p_netlinkList = p_netlinkList->m_next)
|
||||
{
|
||||
unsigned int l_nlsize = p_netlinkList->m_size;
|
||||
struct nlmsghdr *l_hdr;
|
||||
for(l_hdr = p_netlinkList->m_data; NLMSG_OK(l_hdr, l_nlsize); l_hdr = NLMSG_NEXT(l_hdr, l_nlsize))
|
||||
{
|
||||
if((pid_t)l_hdr->nlmsg_pid != l_pid || (int)l_hdr->nlmsg_seq != p_socket)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if(l_hdr->nlmsg_type == NLMSG_DONE)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if(l_hdr->nlmsg_type == RTM_NEWADDR)
|
||||
{
|
||||
if (interpretAddr(l_hdr, p_resultList, p_numLinks) == -1)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int getifaddrs(struct ifaddrs **ifap)
|
||||
{
|
||||
int l_socket;
|
||||
int l_result;
|
||||
int l_numLinks;
|
||||
NetlinkList *l_linkResults;
|
||||
NetlinkList *l_addrResults;
|
||||
|
||||
if(!ifap)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
*ifap = NULL;
|
||||
|
||||
l_socket = netlink_socket();
|
||||
if(l_socket < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
l_linkResults = getResultList(l_socket, RTM_GETLINK);
|
||||
if(!l_linkResults)
|
||||
{
|
||||
close(l_socket);
|
||||
return -1;
|
||||
}
|
||||
|
||||
l_addrResults = getResultList(l_socket, RTM_GETADDR);
|
||||
if(!l_addrResults)
|
||||
{
|
||||
close(l_socket);
|
||||
freeResultList(l_linkResults);
|
||||
return -1;
|
||||
}
|
||||
|
||||
l_result = 0;
|
||||
l_numLinks = interpretLinks(l_socket, l_linkResults, ifap);
|
||||
if(l_numLinks == -1 || interpretAddrs(l_socket, l_addrResults, ifap, l_numLinks) == -1)
|
||||
{
|
||||
l_result = -1;
|
||||
}
|
||||
|
||||
freeResultList(l_linkResults);
|
||||
freeResultList(l_addrResults);
|
||||
close(l_socket);
|
||||
return l_result;
|
||||
}
|
||||
|
||||
void freeifaddrs(struct ifaddrs *ifa)
|
||||
{
|
||||
struct ifaddrs *l_cur;
|
||||
while(ifa)
|
||||
{
|
||||
l_cur = ifa;
|
||||
ifa = ifa->ifa_next;
|
||||
uv__free(l_cur);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,269 @@
|
||||
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/* This file contains both the uv__async internal infrastructure and the
|
||||
* user-facing uv_async_t functions.
|
||||
*/
|
||||
|
||||
#include "uv.h"
|
||||
#include "internal.h"
|
||||
#include "atomic-ops.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h> /* snprintf() */
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static void uv__async_send(uv_loop_t* loop);
|
||||
static int uv__async_start(uv_loop_t* loop);
|
||||
static int uv__async_eventfd(void);
|
||||
|
||||
|
||||
int uv_async_init(uv_loop_t* loop, uv_async_t* handle, uv_async_cb async_cb) {
|
||||
int err;
|
||||
|
||||
err = uv__async_start(loop);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
uv__handle_init(loop, (uv_handle_t*)handle, UV_ASYNC);
|
||||
handle->async_cb = async_cb;
|
||||
handle->pending = 0;
|
||||
|
||||
QUEUE_INSERT_TAIL(&loop->async_handles, &handle->queue);
|
||||
uv__handle_start(handle);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv_async_send(uv_async_t* handle) {
|
||||
/* Do a cheap read first. */
|
||||
if (ACCESS_ONCE(int, handle->pending) != 0)
|
||||
return 0;
|
||||
|
||||
if (cmpxchgi(&handle->pending, 0, 1) == 0)
|
||||
uv__async_send(handle->loop);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void uv__async_close(uv_async_t* handle) {
|
||||
QUEUE_REMOVE(&handle->queue);
|
||||
uv__handle_stop(handle);
|
||||
}
|
||||
|
||||
|
||||
static void uv__async_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) {
|
||||
char buf[1024];
|
||||
ssize_t r;
|
||||
QUEUE queue;
|
||||
QUEUE* q;
|
||||
uv_async_t* h;
|
||||
|
||||
assert(w == &loop->async_io_watcher);
|
||||
|
||||
for (;;) {
|
||||
r = read(w->fd, buf, sizeof(buf));
|
||||
|
||||
if (r == sizeof(buf))
|
||||
continue;
|
||||
|
||||
if (r != -1)
|
||||
break;
|
||||
|
||||
if (errno == EAGAIN || errno == EWOULDBLOCK)
|
||||
break;
|
||||
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
|
||||
abort();
|
||||
}
|
||||
|
||||
QUEUE_MOVE(&loop->async_handles, &queue);
|
||||
while (!QUEUE_EMPTY(&queue)) {
|
||||
q = QUEUE_HEAD(&queue);
|
||||
h = QUEUE_DATA(q, uv_async_t, queue);
|
||||
|
||||
QUEUE_REMOVE(q);
|
||||
QUEUE_INSERT_TAIL(&loop->async_handles, q);
|
||||
|
||||
if (cmpxchgi(&h->pending, 1, 0) == 0)
|
||||
continue;
|
||||
|
||||
if (h->async_cb == NULL)
|
||||
continue;
|
||||
|
||||
h->async_cb(h);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void uv__async_send(uv_loop_t* loop) {
|
||||
const void* buf;
|
||||
ssize_t len;
|
||||
int fd;
|
||||
int r;
|
||||
|
||||
buf = "";
|
||||
len = 1;
|
||||
fd = loop->async_wfd;
|
||||
|
||||
#if defined(__linux__)
|
||||
if (fd == -1) {
|
||||
static const uint64_t val = 1;
|
||||
buf = &val;
|
||||
len = sizeof(val);
|
||||
fd = loop->async_io_watcher.fd; /* eventfd */
|
||||
}
|
||||
#endif
|
||||
|
||||
do
|
||||
r = write(fd, buf, len);
|
||||
while (r == -1 && errno == EINTR);
|
||||
|
||||
if (r == len)
|
||||
return;
|
||||
|
||||
if (r == -1)
|
||||
if (errno == EAGAIN || errno == EWOULDBLOCK)
|
||||
return;
|
||||
|
||||
abort();
|
||||
}
|
||||
|
||||
|
||||
static int uv__async_start(uv_loop_t* loop) {
|
||||
int pipefd[2];
|
||||
int err;
|
||||
|
||||
if (loop->async_io_watcher.fd != -1)
|
||||
return 0;
|
||||
|
||||
err = uv__async_eventfd();
|
||||
if (err >= 0) {
|
||||
pipefd[0] = err;
|
||||
pipefd[1] = -1;
|
||||
}
|
||||
else if (err == -ENOSYS) {
|
||||
err = uv__make_pipe(pipefd, UV__F_NONBLOCK);
|
||||
#if defined(__linux__)
|
||||
/* Save a file descriptor by opening one of the pipe descriptors as
|
||||
* read/write through the procfs. That file descriptor can then
|
||||
* function as both ends of the pipe.
|
||||
*/
|
||||
if (err == 0) {
|
||||
char buf[32];
|
||||
int fd;
|
||||
|
||||
snprintf(buf, sizeof(buf), "/proc/self/fd/%d", pipefd[0]);
|
||||
fd = uv__open_cloexec(buf, O_RDWR);
|
||||
if (fd >= 0) {
|
||||
uv__close(pipefd[0]);
|
||||
uv__close(pipefd[1]);
|
||||
pipefd[0] = fd;
|
||||
pipefd[1] = fd;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
uv__io_init(&loop->async_io_watcher, uv__async_io, pipefd[0]);
|
||||
uv__io_start(loop, &loop->async_io_watcher, POLLIN);
|
||||
loop->async_wfd = pipefd[1];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv__async_fork(uv_loop_t* loop) {
|
||||
if (loop->async_io_watcher.fd == -1) /* never started */
|
||||
return 0;
|
||||
|
||||
uv__async_stop(loop);
|
||||
|
||||
return uv__async_start(loop);
|
||||
}
|
||||
|
||||
|
||||
void uv__async_stop(uv_loop_t* loop) {
|
||||
if (loop->async_io_watcher.fd == -1)
|
||||
return;
|
||||
|
||||
if (loop->async_wfd != -1) {
|
||||
if (loop->async_wfd != loop->async_io_watcher.fd)
|
||||
uv__close(loop->async_wfd);
|
||||
loop->async_wfd = -1;
|
||||
}
|
||||
|
||||
uv__io_stop(loop, &loop->async_io_watcher, POLLIN);
|
||||
uv__close(loop->async_io_watcher.fd);
|
||||
loop->async_io_watcher.fd = -1;
|
||||
}
|
||||
|
||||
|
||||
static int uv__async_eventfd(void) {
|
||||
#if defined(__linux__)
|
||||
static int no_eventfd2;
|
||||
static int no_eventfd;
|
||||
int fd;
|
||||
|
||||
if (no_eventfd2)
|
||||
goto skip_eventfd2;
|
||||
|
||||
fd = uv__eventfd2(0, UV__EFD_CLOEXEC | UV__EFD_NONBLOCK);
|
||||
if (fd != -1)
|
||||
return fd;
|
||||
|
||||
if (errno != ENOSYS)
|
||||
return -errno;
|
||||
|
||||
no_eventfd2 = 1;
|
||||
|
||||
skip_eventfd2:
|
||||
|
||||
if (no_eventfd)
|
||||
goto skip_eventfd;
|
||||
|
||||
fd = uv__eventfd(0);
|
||||
if (fd != -1) {
|
||||
uv__cloexec(fd, 1);
|
||||
uv__nonblock(fd, 1);
|
||||
return fd;
|
||||
}
|
||||
|
||||
if (errno != ENOSYS)
|
||||
return -errno;
|
||||
|
||||
no_eventfd = 1;
|
||||
|
||||
skip_eventfd:
|
||||
|
||||
#endif
|
||||
|
||||
return -ENOSYS;
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/* Copyright (c) 2013, Ben Noordhuis <info@bnoordhuis.nl>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef UV_ATOMIC_OPS_H_
|
||||
#define UV_ATOMIC_OPS_H_
|
||||
|
||||
#include "internal.h" /* UV_UNUSED */
|
||||
|
||||
#if defined(__SUNPRO_C) || defined(__SUNPRO_CC)
|
||||
#include <atomic.h>
|
||||
#define __sync_val_compare_and_swap(p, o, n) atomic_cas_ptr(p, o, n)
|
||||
#endif
|
||||
|
||||
UV_UNUSED(static int cmpxchgi(int* ptr, int oldval, int newval));
|
||||
UV_UNUSED(static long cmpxchgl(long* ptr, long oldval, long newval));
|
||||
UV_UNUSED(static void cpu_relax(void));
|
||||
|
||||
/* Prefer hand-rolled assembly over the gcc builtins because the latter also
|
||||
* issue full memory barriers.
|
||||
*/
|
||||
UV_UNUSED(static int cmpxchgi(int* ptr, int oldval, int newval)) {
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
int out;
|
||||
__asm__ __volatile__ ("lock; cmpxchg %2, %1;"
|
||||
: "=a" (out), "+m" (*(volatile int*) ptr)
|
||||
: "r" (newval), "0" (oldval)
|
||||
: "memory");
|
||||
return out;
|
||||
#elif defined(_AIX) && defined(__xlC__)
|
||||
const int out = (*(volatile int*) ptr);
|
||||
__compare_and_swap(ptr, &oldval, newval);
|
||||
return out;
|
||||
#elif defined(__MVS__)
|
||||
unsigned int op4;
|
||||
if (__plo_CSST(ptr, (unsigned int*) &oldval, newval,
|
||||
(unsigned int*) ptr, *ptr, &op4))
|
||||
return oldval;
|
||||
else
|
||||
return op4;
|
||||
#else
|
||||
return __sync_val_compare_and_swap(ptr, oldval, newval);
|
||||
#endif
|
||||
}
|
||||
|
||||
UV_UNUSED(static long cmpxchgl(long* ptr, long oldval, long newval)) {
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
long out;
|
||||
__asm__ __volatile__ ("lock; cmpxchg %2, %1;"
|
||||
: "=a" (out), "+m" (*(volatile long*) ptr)
|
||||
: "r" (newval), "0" (oldval)
|
||||
: "memory");
|
||||
return out;
|
||||
#elif defined(_AIX) && defined(__xlC__)
|
||||
const long out = (*(volatile int*) ptr);
|
||||
# if defined(__64BIT__)
|
||||
__compare_and_swaplp(ptr, &oldval, newval);
|
||||
# else
|
||||
__compare_and_swap(ptr, &oldval, newval);
|
||||
# endif /* if defined(__64BIT__) */
|
||||
return out;
|
||||
#elif defined (__MVS__)
|
||||
#ifdef _LP64
|
||||
unsigned long long op4;
|
||||
if (__plo_CSSTGR(ptr, (unsigned long long*) &oldval, newval,
|
||||
(unsigned long long*) ptr, *ptr, &op4))
|
||||
#else
|
||||
unsigned long op4;
|
||||
if (__plo_CSST(ptr, (unsigned int*) &oldval, newval,
|
||||
(unsigned int*) ptr, *ptr, &op4))
|
||||
#endif
|
||||
return oldval;
|
||||
else
|
||||
return op4;
|
||||
#else
|
||||
return __sync_val_compare_and_swap(ptr, oldval, newval);
|
||||
#endif
|
||||
}
|
||||
|
||||
UV_UNUSED(static void cpu_relax(void)) {
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
__asm__ __volatile__ ("rep; nop"); /* a.k.a. PAUSE */
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif /* UV_ATOMIC_OPS_H_ */
|
||||
@@ -0,0 +1,146 @@
|
||||
/* Copyright libuv project contributors. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "uv.h"
|
||||
#include "internal.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include <ifaddrs.h>
|
||||
#include <net/if.h>
|
||||
#if !defined(__CYGWIN__) && !defined(__MSYS__)
|
||||
#include <net/if_dl.h>
|
||||
#endif
|
||||
|
||||
static int uv__ifaddr_exclude(struct ifaddrs *ent, int exclude_type) {
|
||||
if (!((ent->ifa_flags & IFF_UP) && (ent->ifa_flags & IFF_RUNNING)))
|
||||
return 1;
|
||||
if (ent->ifa_addr == NULL)
|
||||
return 1;
|
||||
/*
|
||||
* If `exclude_type` is `UV__EXCLUDE_IFPHYS`, just see whether `sa_family`
|
||||
* equals to `AF_LINK` or not. Otherwise, the result depends on the operation
|
||||
* system with `AF_LINK` or `PF_INET`.
|
||||
*/
|
||||
if (exclude_type == UV__EXCLUDE_IFPHYS)
|
||||
return (ent->ifa_addr->sa_family != AF_LINK);
|
||||
#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__DragonFly__)
|
||||
/*
|
||||
* On BSD getifaddrs returns information related to the raw underlying
|
||||
* devices. We're not interested in this information.
|
||||
*/
|
||||
if (ent->ifa_addr->sa_family == AF_LINK)
|
||||
return 1;
|
||||
#elif defined(__NetBSD__) || defined(__OpenBSD__)
|
||||
if (ent->ifa_addr->sa_family != PF_INET)
|
||||
return 1;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
|
||||
struct ifaddrs* addrs;
|
||||
struct ifaddrs* ent;
|
||||
uv_interface_address_t* address;
|
||||
int i;
|
||||
|
||||
if (getifaddrs(&addrs) != 0)
|
||||
return -errno;
|
||||
|
||||
*count = 0;
|
||||
|
||||
/* Count the number of interfaces */
|
||||
for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
|
||||
if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFADDR))
|
||||
continue;
|
||||
(*count)++;
|
||||
}
|
||||
|
||||
*addresses = uv__malloc(*count * sizeof(**addresses));
|
||||
|
||||
if (*addresses == NULL) {
|
||||
freeifaddrs(addrs);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
address = *addresses;
|
||||
|
||||
for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
|
||||
if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFADDR))
|
||||
continue;
|
||||
|
||||
address->name = uv__strdup(ent->ifa_name);
|
||||
|
||||
if (ent->ifa_addr->sa_family == AF_INET6) {
|
||||
address->address.address6 = *((struct sockaddr_in6*) ent->ifa_addr);
|
||||
} else {
|
||||
address->address.address4 = *((struct sockaddr_in*) ent->ifa_addr);
|
||||
}
|
||||
|
||||
if (ent->ifa_netmask->sa_family == AF_INET6) {
|
||||
address->netmask.netmask6 = *((struct sockaddr_in6*) ent->ifa_netmask);
|
||||
} else {
|
||||
address->netmask.netmask4 = *((struct sockaddr_in*) ent->ifa_netmask);
|
||||
}
|
||||
|
||||
address->is_internal = !!(ent->ifa_flags & IFF_LOOPBACK);
|
||||
|
||||
address++;
|
||||
}
|
||||
|
||||
/* Fill in physical addresses for each interface */
|
||||
for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
|
||||
if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFPHYS))
|
||||
continue;
|
||||
|
||||
address = *addresses;
|
||||
|
||||
for (i = 0; i < *count; i++) {
|
||||
if (strcmp(address->name, ent->ifa_name) == 0) {
|
||||
#if defined(__CYGWIN__) || defined(__MSYS__)
|
||||
memset(address->phys_addr, 0, sizeof(address->phys_addr));
|
||||
#else
|
||||
struct sockaddr_dl* sa_addr;
|
||||
sa_addr = (struct sockaddr_dl*)(ent->ifa_addr);
|
||||
memcpy(address->phys_addr, LLADDR(sa_addr), sizeof(address->phys_addr));
|
||||
#endif
|
||||
}
|
||||
address++;
|
||||
}
|
||||
}
|
||||
|
||||
freeifaddrs(addrs);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void uv_free_interface_addresses(uv_interface_address_t* addresses,
|
||||
int count) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
uv__free(addresses[i].name);
|
||||
}
|
||||
|
||||
uv__free(addresses);
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,54 @@
|
||||
/* Copyright libuv project contributors. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "uv.h"
|
||||
#include "internal.h"
|
||||
|
||||
#include <sys/sysinfo.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int uv_uptime(double* uptime) {
|
||||
struct sysinfo info;
|
||||
|
||||
if (sysinfo(&info) < 0)
|
||||
return -errno;
|
||||
|
||||
*uptime = info.uptime;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int uv_resident_set_memory(size_t* rss) {
|
||||
/* FIXME: read /proc/meminfo? */
|
||||
*rss = 0;
|
||||
return UV_ENOSYS;
|
||||
}
|
||||
|
||||
int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) {
|
||||
/* FIXME: read /proc/stat? */
|
||||
*cpu_infos = NULL;
|
||||
*count = 0;
|
||||
return UV_ENOSYS;
|
||||
}
|
||||
|
||||
void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count) {
|
||||
(void)cpu_infos;
|
||||
(void)count;
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <TargetConditionals.h>
|
||||
|
||||
#if !TARGET_OS_IPHONE
|
||||
# include <CoreFoundation/CoreFoundation.h>
|
||||
# include <ApplicationServices/ApplicationServices.h>
|
||||
#endif
|
||||
|
||||
|
||||
static int uv__pthread_setname_np(const char* name) {
|
||||
int (*dynamic_pthread_setname_np)(const char* name);
|
||||
char namebuf[64]; /* MAXTHREADNAMESIZE */
|
||||
int err;
|
||||
|
||||
/* pthread_setname_np() first appeared in OS X 10.6 and iOS 3.2. */
|
||||
*(void **)(&dynamic_pthread_setname_np) =
|
||||
dlsym(RTLD_DEFAULT, "pthread_setname_np");
|
||||
|
||||
if (dynamic_pthread_setname_np == NULL)
|
||||
return -ENOSYS;
|
||||
|
||||
strncpy(namebuf, name, sizeof(namebuf) - 1);
|
||||
namebuf[sizeof(namebuf) - 1] = '\0';
|
||||
|
||||
err = dynamic_pthread_setname_np(namebuf);
|
||||
if (err)
|
||||
return -err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv__set_process_title(const char* title) {
|
||||
#if TARGET_OS_IPHONE
|
||||
return uv__pthread_setname_np(title);
|
||||
#else
|
||||
CFStringRef (*pCFStringCreateWithCString)(CFAllocatorRef,
|
||||
const char*,
|
||||
CFStringEncoding);
|
||||
CFBundleRef (*pCFBundleGetBundleWithIdentifier)(CFStringRef);
|
||||
void *(*pCFBundleGetDataPointerForName)(CFBundleRef, CFStringRef);
|
||||
void *(*pCFBundleGetFunctionPointerForName)(CFBundleRef, CFStringRef);
|
||||
CFTypeRef (*pLSGetCurrentApplicationASN)(void);
|
||||
OSStatus (*pLSSetApplicationInformationItem)(int,
|
||||
CFTypeRef,
|
||||
CFStringRef,
|
||||
CFStringRef,
|
||||
CFDictionaryRef*);
|
||||
void* application_services_handle;
|
||||
void* core_foundation_handle;
|
||||
CFBundleRef launch_services_bundle;
|
||||
CFStringRef* display_name_key;
|
||||
CFDictionaryRef (*pCFBundleGetInfoDictionary)(CFBundleRef);
|
||||
CFBundleRef (*pCFBundleGetMainBundle)(void);
|
||||
CFBundleRef hi_services_bundle;
|
||||
OSStatus (*pSetApplicationIsDaemon)(int);
|
||||
CFDictionaryRef (*pLSApplicationCheckIn)(int, CFDictionaryRef);
|
||||
void (*pLSSetApplicationLaunchServicesServerConnectionStatus)(uint64_t,
|
||||
void*);
|
||||
CFTypeRef asn;
|
||||
int err;
|
||||
|
||||
err = -ENOENT;
|
||||
application_services_handle = dlopen("/System/Library/Frameworks/"
|
||||
"ApplicationServices.framework/"
|
||||
"Versions/A/ApplicationServices",
|
||||
RTLD_LAZY | RTLD_LOCAL);
|
||||
core_foundation_handle = dlopen("/System/Library/Frameworks/"
|
||||
"CoreFoundation.framework/"
|
||||
"Versions/A/CoreFoundation",
|
||||
RTLD_LAZY | RTLD_LOCAL);
|
||||
|
||||
if (application_services_handle == NULL || core_foundation_handle == NULL)
|
||||
goto out;
|
||||
|
||||
*(void **)(&pCFStringCreateWithCString) =
|
||||
dlsym(core_foundation_handle, "CFStringCreateWithCString");
|
||||
*(void **)(&pCFBundleGetBundleWithIdentifier) =
|
||||
dlsym(core_foundation_handle, "CFBundleGetBundleWithIdentifier");
|
||||
*(void **)(&pCFBundleGetDataPointerForName) =
|
||||
dlsym(core_foundation_handle, "CFBundleGetDataPointerForName");
|
||||
*(void **)(&pCFBundleGetFunctionPointerForName) =
|
||||
dlsym(core_foundation_handle, "CFBundleGetFunctionPointerForName");
|
||||
|
||||
if (pCFStringCreateWithCString == NULL ||
|
||||
pCFBundleGetBundleWithIdentifier == NULL ||
|
||||
pCFBundleGetDataPointerForName == NULL ||
|
||||
pCFBundleGetFunctionPointerForName == NULL) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
#define S(s) pCFStringCreateWithCString(NULL, (s), kCFStringEncodingUTF8)
|
||||
|
||||
launch_services_bundle =
|
||||
pCFBundleGetBundleWithIdentifier(S("com.apple.LaunchServices"));
|
||||
|
||||
if (launch_services_bundle == NULL)
|
||||
goto out;
|
||||
|
||||
*(void **)(&pLSGetCurrentApplicationASN) =
|
||||
pCFBundleGetFunctionPointerForName(launch_services_bundle,
|
||||
S("_LSGetCurrentApplicationASN"));
|
||||
|
||||
if (pLSGetCurrentApplicationASN == NULL)
|
||||
goto out;
|
||||
|
||||
*(void **)(&pLSSetApplicationInformationItem) =
|
||||
pCFBundleGetFunctionPointerForName(launch_services_bundle,
|
||||
S("_LSSetApplicationInformationItem"));
|
||||
|
||||
if (pLSSetApplicationInformationItem == NULL)
|
||||
goto out;
|
||||
|
||||
display_name_key = pCFBundleGetDataPointerForName(launch_services_bundle,
|
||||
S("_kLSDisplayNameKey"));
|
||||
|
||||
if (display_name_key == NULL || *display_name_key == NULL)
|
||||
goto out;
|
||||
|
||||
*(void **)(&pCFBundleGetInfoDictionary) = dlsym(core_foundation_handle,
|
||||
"CFBundleGetInfoDictionary");
|
||||
*(void **)(&pCFBundleGetMainBundle) = dlsym(core_foundation_handle,
|
||||
"CFBundleGetMainBundle");
|
||||
if (pCFBundleGetInfoDictionary == NULL || pCFBundleGetMainBundle == NULL)
|
||||
goto out;
|
||||
|
||||
/* Black 10.9 magic, to remove (Not responding) mark in Activity Monitor */
|
||||
hi_services_bundle =
|
||||
pCFBundleGetBundleWithIdentifier(S("com.apple.HIServices"));
|
||||
err = -ENOENT;
|
||||
if (hi_services_bundle == NULL)
|
||||
goto out;
|
||||
|
||||
*(void **)(&pSetApplicationIsDaemon) = pCFBundleGetFunctionPointerForName(
|
||||
hi_services_bundle,
|
||||
S("SetApplicationIsDaemon"));
|
||||
*(void **)(&pLSApplicationCheckIn) = pCFBundleGetFunctionPointerForName(
|
||||
launch_services_bundle,
|
||||
S("_LSApplicationCheckIn"));
|
||||
*(void **)(&pLSSetApplicationLaunchServicesServerConnectionStatus) =
|
||||
pCFBundleGetFunctionPointerForName(
|
||||
launch_services_bundle,
|
||||
S("_LSSetApplicationLaunchServicesServerConnectionStatus"));
|
||||
if (pSetApplicationIsDaemon == NULL ||
|
||||
pLSApplicationCheckIn == NULL ||
|
||||
pLSSetApplicationLaunchServicesServerConnectionStatus == NULL) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (pSetApplicationIsDaemon(1) != noErr)
|
||||
goto out;
|
||||
|
||||
pLSSetApplicationLaunchServicesServerConnectionStatus(0, NULL);
|
||||
|
||||
/* Check into process manager?! */
|
||||
pLSApplicationCheckIn(-2,
|
||||
pCFBundleGetInfoDictionary(pCFBundleGetMainBundle()));
|
||||
|
||||
asn = pLSGetCurrentApplicationASN();
|
||||
|
||||
err = -EINVAL;
|
||||
if (pLSSetApplicationInformationItem(-2, /* Magic value. */
|
||||
asn,
|
||||
*display_name_key,
|
||||
S(title),
|
||||
NULL) != noErr) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
uv__pthread_setname_np(title); /* Don't care if it fails. */
|
||||
err = 0;
|
||||
|
||||
out:
|
||||
if (core_foundation_handle != NULL)
|
||||
dlclose(core_foundation_handle);
|
||||
|
||||
if (application_services_handle != NULL)
|
||||
dlclose(application_services_handle);
|
||||
|
||||
return err;
|
||||
#endif /* !TARGET_OS_IPHONE */
|
||||
}
|
||||
@@ -0,0 +1,231 @@
|
||||
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "uv.h"
|
||||
#include "internal.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <mach/mach.h>
|
||||
#include <mach/mach_time.h>
|
||||
#include <mach-o/dyld.h> /* _NSGetExecutablePath */
|
||||
#include <sys/resource.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <unistd.h> /* sysconf */
|
||||
|
||||
|
||||
int uv__platform_loop_init(uv_loop_t* loop) {
|
||||
loop->cf_state = NULL;
|
||||
|
||||
if (uv__kqueue_init(loop))
|
||||
return -errno;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void uv__platform_loop_delete(uv_loop_t* loop) {
|
||||
uv__fsevents_loop_delete(loop);
|
||||
}
|
||||
|
||||
|
||||
uint64_t uv__hrtime(uv_clocktype_t type) {
|
||||
static mach_timebase_info_data_t info;
|
||||
|
||||
if ((ACCESS_ONCE(uint32_t, info.numer) == 0 ||
|
||||
ACCESS_ONCE(uint32_t, info.denom) == 0) &&
|
||||
mach_timebase_info(&info) != KERN_SUCCESS)
|
||||
abort();
|
||||
|
||||
return mach_absolute_time() * info.numer / info.denom;
|
||||
}
|
||||
|
||||
|
||||
int uv_exepath(char* buffer, size_t* size) {
|
||||
/* realpath(exepath) may be > PATH_MAX so double it to be on the safe side. */
|
||||
char abspath[PATH_MAX * 2 + 1];
|
||||
char exepath[PATH_MAX + 1];
|
||||
uint32_t exepath_size;
|
||||
size_t abspath_size;
|
||||
|
||||
if (buffer == NULL || size == NULL || *size == 0)
|
||||
return -EINVAL;
|
||||
|
||||
exepath_size = sizeof(exepath);
|
||||
if (_NSGetExecutablePath(exepath, &exepath_size))
|
||||
return -EIO;
|
||||
|
||||
if (realpath(exepath, abspath) != abspath)
|
||||
return -errno;
|
||||
|
||||
abspath_size = strlen(abspath);
|
||||
if (abspath_size == 0)
|
||||
return -EIO;
|
||||
|
||||
*size -= 1;
|
||||
if (*size > abspath_size)
|
||||
*size = abspath_size;
|
||||
|
||||
memcpy(buffer, abspath, *size);
|
||||
buffer[*size] = '\0';
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
uint64_t uv_get_free_memory(void) {
|
||||
vm_statistics_data_t info;
|
||||
mach_msg_type_number_t count = sizeof(info) / sizeof(integer_t);
|
||||
|
||||
if (host_statistics(mach_host_self(), HOST_VM_INFO,
|
||||
(host_info_t)&info, &count) != KERN_SUCCESS) {
|
||||
return -EINVAL; /* FIXME(bnoordhuis) Translate error. */
|
||||
}
|
||||
|
||||
return (uint64_t) info.free_count * sysconf(_SC_PAGESIZE);
|
||||
}
|
||||
|
||||
|
||||
uint64_t uv_get_total_memory(void) {
|
||||
uint64_t info;
|
||||
int which[] = {CTL_HW, HW_MEMSIZE};
|
||||
size_t size = sizeof(info);
|
||||
|
||||
if (sysctl(which, 2, &info, &size, NULL, 0))
|
||||
return -errno;
|
||||
|
||||
return (uint64_t) info;
|
||||
}
|
||||
|
||||
|
||||
void uv_loadavg(double avg[3]) {
|
||||
struct loadavg info;
|
||||
size_t size = sizeof(info);
|
||||
int which[] = {CTL_VM, VM_LOADAVG};
|
||||
|
||||
if (sysctl(which, 2, &info, &size, NULL, 0) < 0) return;
|
||||
|
||||
avg[0] = (double) info.ldavg[0] / info.fscale;
|
||||
avg[1] = (double) info.ldavg[1] / info.fscale;
|
||||
avg[2] = (double) info.ldavg[2] / info.fscale;
|
||||
}
|
||||
|
||||
|
||||
int uv_resident_set_memory(size_t* rss) {
|
||||
mach_msg_type_number_t count;
|
||||
task_basic_info_data_t info;
|
||||
kern_return_t err;
|
||||
|
||||
count = TASK_BASIC_INFO_COUNT;
|
||||
err = task_info(mach_task_self(),
|
||||
TASK_BASIC_INFO,
|
||||
(task_info_t) &info,
|
||||
&count);
|
||||
(void) &err;
|
||||
/* task_info(TASK_BASIC_INFO) cannot really fail. Anything other than
|
||||
* KERN_SUCCESS implies a libuv bug.
|
||||
*/
|
||||
assert(err == KERN_SUCCESS);
|
||||
*rss = info.resident_size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv_uptime(double* uptime) {
|
||||
time_t now;
|
||||
struct timeval info;
|
||||
size_t size = sizeof(info);
|
||||
static int which[] = {CTL_KERN, KERN_BOOTTIME};
|
||||
|
||||
if (sysctl(which, 2, &info, &size, NULL, 0))
|
||||
return -errno;
|
||||
|
||||
now = time(NULL);
|
||||
*uptime = now - info.tv_sec;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) {
|
||||
unsigned int ticks = (unsigned int)sysconf(_SC_CLK_TCK),
|
||||
multiplier = ((uint64_t)1000L / ticks);
|
||||
char model[512];
|
||||
uint64_t cpuspeed;
|
||||
size_t size;
|
||||
unsigned int i;
|
||||
natural_t numcpus;
|
||||
mach_msg_type_number_t msg_type;
|
||||
processor_cpu_load_info_data_t *info;
|
||||
uv_cpu_info_t* cpu_info;
|
||||
|
||||
size = sizeof(model);
|
||||
if (sysctlbyname("machdep.cpu.brand_string", &model, &size, NULL, 0) &&
|
||||
sysctlbyname("hw.model", &model, &size, NULL, 0)) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
size = sizeof(cpuspeed);
|
||||
if (sysctlbyname("hw.cpufrequency", &cpuspeed, &size, NULL, 0))
|
||||
return -errno;
|
||||
|
||||
if (host_processor_info(mach_host_self(), PROCESSOR_CPU_LOAD_INFO, &numcpus,
|
||||
(processor_info_array_t*)&info,
|
||||
&msg_type) != KERN_SUCCESS) {
|
||||
return -EINVAL; /* FIXME(bnoordhuis) Translate error. */
|
||||
}
|
||||
|
||||
*cpu_infos = uv__malloc(numcpus * sizeof(**cpu_infos));
|
||||
if (!(*cpu_infos)) {
|
||||
vm_deallocate(mach_task_self(), (vm_address_t)info, msg_type);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
*count = numcpus;
|
||||
|
||||
for (i = 0; i < numcpus; i++) {
|
||||
cpu_info = &(*cpu_infos)[i];
|
||||
|
||||
cpu_info->cpu_times.user = (uint64_t)(info[i].cpu_ticks[0]) * multiplier;
|
||||
cpu_info->cpu_times.nice = (uint64_t)(info[i].cpu_ticks[3]) * multiplier;
|
||||
cpu_info->cpu_times.sys = (uint64_t)(info[i].cpu_ticks[1]) * multiplier;
|
||||
cpu_info->cpu_times.idle = (uint64_t)(info[i].cpu_ticks[2]) * multiplier;
|
||||
cpu_info->cpu_times.irq = 0;
|
||||
|
||||
cpu_info->model = uv__strdup(model);
|
||||
cpu_info->speed = cpuspeed/1000000;
|
||||
}
|
||||
vm_deallocate(mach_task_self(), (vm_address_t)info, msg_type);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
uv__free(cpu_infos[i].model);
|
||||
}
|
||||
|
||||
uv__free(cpu_infos);
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "uv.h"
|
||||
#include "internal.h"
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <locale.h>
|
||||
|
||||
static int uv__dlerror(uv_lib_t* lib);
|
||||
|
||||
|
||||
int uv_dlopen(const char* filename, uv_lib_t* lib) {
|
||||
dlerror(); /* Reset error status. */
|
||||
lib->errmsg = NULL;
|
||||
lib->handle = dlopen(filename, RTLD_LAZY);
|
||||
return lib->handle ? 0 : uv__dlerror(lib);
|
||||
}
|
||||
|
||||
|
||||
void uv_dlclose(uv_lib_t* lib) {
|
||||
uv__free(lib->errmsg);
|
||||
lib->errmsg = NULL;
|
||||
|
||||
if (lib->handle) {
|
||||
/* Ignore errors. No good way to signal them without leaking memory. */
|
||||
dlclose(lib->handle);
|
||||
lib->handle = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int uv_dlsym(uv_lib_t* lib, const char* name, void** ptr) {
|
||||
dlerror(); /* Reset error status. */
|
||||
*ptr = dlsym(lib->handle, name);
|
||||
return uv__dlerror(lib);
|
||||
}
|
||||
|
||||
|
||||
const char* uv_dlerror(const uv_lib_t* lib) {
|
||||
return lib->errmsg ? lib->errmsg : "no error";
|
||||
}
|
||||
|
||||
|
||||
static int uv__dlerror(uv_lib_t* lib) {
|
||||
const char* errmsg;
|
||||
|
||||
uv__free(lib->errmsg);
|
||||
|
||||
errmsg = dlerror();
|
||||
|
||||
if (errmsg) {
|
||||
lib->errmsg = uv__strdup(errmsg);
|
||||
return -1;
|
||||
}
|
||||
else {
|
||||
lib->errmsg = NULL;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,346 @@
|
||||
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "uv.h"
|
||||
#include "internal.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <kvm.h>
|
||||
#include <paths.h>
|
||||
#include <sys/user.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <vm/vm_param.h> /* VM_LOADAVG */
|
||||
#include <time.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h> /* sysconf */
|
||||
#include <fcntl.h>
|
||||
|
||||
#ifndef CPUSTATES
|
||||
# define CPUSTATES 5U
|
||||
#endif
|
||||
#ifndef CP_USER
|
||||
# define CP_USER 0
|
||||
# define CP_NICE 1
|
||||
# define CP_SYS 2
|
||||
# define CP_IDLE 3
|
||||
# define CP_INTR 4
|
||||
#endif
|
||||
|
||||
static char *process_title;
|
||||
|
||||
|
||||
int uv__platform_loop_init(uv_loop_t* loop) {
|
||||
return uv__kqueue_init(loop);
|
||||
}
|
||||
|
||||
|
||||
void uv__platform_loop_delete(uv_loop_t* loop) {
|
||||
}
|
||||
|
||||
|
||||
#ifdef __DragonFly__
|
||||
int uv_exepath(char* buffer, size_t* size) {
|
||||
char abspath[PATH_MAX * 2 + 1];
|
||||
ssize_t abspath_size;
|
||||
|
||||
if (buffer == NULL || size == NULL || *size == 0)
|
||||
return -EINVAL;
|
||||
|
||||
abspath_size = readlink("/proc/curproc/file", abspath, sizeof(abspath));
|
||||
if (abspath_size < 0)
|
||||
return -errno;
|
||||
|
||||
assert(abspath_size > 0);
|
||||
*size -= 1;
|
||||
|
||||
if (*size > abspath_size)
|
||||
*size = abspath_size;
|
||||
|
||||
memcpy(buffer, abspath, *size);
|
||||
buffer[*size] = '\0';
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
int uv_exepath(char* buffer, size_t* size) {
|
||||
char abspath[PATH_MAX * 2 + 1];
|
||||
int mib[4];
|
||||
size_t abspath_size;
|
||||
|
||||
if (buffer == NULL || size == NULL || *size == 0)
|
||||
return -EINVAL;
|
||||
|
||||
mib[0] = CTL_KERN;
|
||||
mib[1] = KERN_PROC;
|
||||
mib[2] = KERN_PROC_PATHNAME;
|
||||
mib[3] = -1;
|
||||
|
||||
abspath_size = sizeof abspath;
|
||||
if (sysctl(mib, 4, abspath, &abspath_size, NULL, 0))
|
||||
return -errno;
|
||||
|
||||
assert(abspath_size > 0);
|
||||
abspath_size -= 1;
|
||||
*size -= 1;
|
||||
|
||||
if (*size > abspath_size)
|
||||
*size = abspath_size;
|
||||
|
||||
memcpy(buffer, abspath, *size);
|
||||
buffer[*size] = '\0';
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
uint64_t uv_get_free_memory(void) {
|
||||
int freecount;
|
||||
size_t size = sizeof(freecount);
|
||||
|
||||
if (sysctlbyname("vm.stats.vm.v_free_count", &freecount, &size, NULL, 0))
|
||||
return -errno;
|
||||
|
||||
return (uint64_t) freecount * sysconf(_SC_PAGESIZE);
|
||||
|
||||
}
|
||||
|
||||
|
||||
uint64_t uv_get_total_memory(void) {
|
||||
unsigned long info;
|
||||
int which[] = {CTL_HW, HW_PHYSMEM};
|
||||
|
||||
size_t size = sizeof(info);
|
||||
|
||||
if (sysctl(which, 2, &info, &size, NULL, 0))
|
||||
return -errno;
|
||||
|
||||
return (uint64_t) info;
|
||||
}
|
||||
|
||||
|
||||
void uv_loadavg(double avg[3]) {
|
||||
struct loadavg info;
|
||||
size_t size = sizeof(info);
|
||||
int which[] = {CTL_VM, VM_LOADAVG};
|
||||
|
||||
if (sysctl(which, 2, &info, &size, NULL, 0) < 0) return;
|
||||
|
||||
avg[0] = (double) info.ldavg[0] / info.fscale;
|
||||
avg[1] = (double) info.ldavg[1] / info.fscale;
|
||||
avg[2] = (double) info.ldavg[2] / info.fscale;
|
||||
}
|
||||
|
||||
|
||||
char** uv_setup_args(int argc, char** argv) {
|
||||
process_title = argc ? uv__strdup(argv[0]) : NULL;
|
||||
return argv;
|
||||
}
|
||||
|
||||
|
||||
int uv_set_process_title(const char* title) {
|
||||
int oid[4];
|
||||
|
||||
uv__free(process_title);
|
||||
process_title = uv__strdup(title);
|
||||
|
||||
oid[0] = CTL_KERN;
|
||||
oid[1] = KERN_PROC;
|
||||
oid[2] = KERN_PROC_ARGS;
|
||||
oid[3] = getpid();
|
||||
|
||||
sysctl(oid,
|
||||
ARRAY_SIZE(oid),
|
||||
NULL,
|
||||
NULL,
|
||||
process_title,
|
||||
strlen(process_title) + 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv_get_process_title(char* buffer, size_t size) {
|
||||
size_t len;
|
||||
|
||||
if (buffer == NULL || size == 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (process_title) {
|
||||
len = strlen(process_title) + 1;
|
||||
|
||||
if (size < len)
|
||||
return -ENOBUFS;
|
||||
|
||||
memcpy(buffer, process_title, len);
|
||||
} else {
|
||||
len = 0;
|
||||
}
|
||||
|
||||
buffer[len] = '\0';
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv_resident_set_memory(size_t* rss) {
|
||||
kvm_t *kd = NULL;
|
||||
struct kinfo_proc *kinfo = NULL;
|
||||
pid_t pid;
|
||||
int nprocs;
|
||||
size_t page_size = getpagesize();
|
||||
|
||||
pid = getpid();
|
||||
|
||||
kd = kvm_open(NULL, _PATH_DEVNULL, NULL, O_RDONLY, "kvm_open");
|
||||
if (kd == NULL) goto error;
|
||||
|
||||
kinfo = kvm_getprocs(kd, KERN_PROC_PID, pid, &nprocs);
|
||||
if (kinfo == NULL) goto error;
|
||||
|
||||
#ifdef __DragonFly__
|
||||
*rss = kinfo->kp_vm_rssize * page_size;
|
||||
#else
|
||||
*rss = kinfo->ki_rssize * page_size;
|
||||
#endif
|
||||
|
||||
kvm_close(kd);
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
if (kd) kvm_close(kd);
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
|
||||
int uv_uptime(double* uptime) {
|
||||
int r;
|
||||
struct timespec sp;
|
||||
r = clock_gettime(CLOCK_MONOTONIC, &sp);
|
||||
if (r)
|
||||
return -errno;
|
||||
|
||||
*uptime = sp.tv_sec;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) {
|
||||
unsigned int ticks = (unsigned int)sysconf(_SC_CLK_TCK),
|
||||
multiplier = ((uint64_t)1000L / ticks), cpuspeed, maxcpus,
|
||||
cur = 0;
|
||||
uv_cpu_info_t* cpu_info;
|
||||
const char* maxcpus_key;
|
||||
const char* cptimes_key;
|
||||
char model[512];
|
||||
long* cp_times;
|
||||
int numcpus;
|
||||
size_t size;
|
||||
int i;
|
||||
|
||||
#if defined(__DragonFly__)
|
||||
/* This is not quite correct but DragonFlyBSD doesn't seem to have anything
|
||||
* comparable to kern.smp.maxcpus or kern.cp_times (kern.cp_time is a total,
|
||||
* not per CPU). At least this stops uv_cpu_info() from failing completely.
|
||||
*/
|
||||
maxcpus_key = "hw.ncpu";
|
||||
cptimes_key = "kern.cp_time";
|
||||
#else
|
||||
maxcpus_key = "kern.smp.maxcpus";
|
||||
cptimes_key = "kern.cp_times";
|
||||
#endif
|
||||
|
||||
size = sizeof(model);
|
||||
if (sysctlbyname("hw.model", &model, &size, NULL, 0))
|
||||
return -errno;
|
||||
|
||||
size = sizeof(numcpus);
|
||||
if (sysctlbyname("hw.ncpu", &numcpus, &size, NULL, 0))
|
||||
return -errno;
|
||||
|
||||
*cpu_infos = uv__malloc(numcpus * sizeof(**cpu_infos));
|
||||
if (!(*cpu_infos))
|
||||
return -ENOMEM;
|
||||
|
||||
*count = numcpus;
|
||||
|
||||
size = sizeof(cpuspeed);
|
||||
if (sysctlbyname("hw.clockrate", &cpuspeed, &size, NULL, 0)) {
|
||||
uv__free(*cpu_infos);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
/* kern.cp_times on FreeBSD i386 gives an array up to maxcpus instead of
|
||||
* ncpu.
|
||||
*/
|
||||
size = sizeof(maxcpus);
|
||||
if (sysctlbyname(maxcpus_key, &maxcpus, &size, NULL, 0)) {
|
||||
uv__free(*cpu_infos);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
size = maxcpus * CPUSTATES * sizeof(long);
|
||||
|
||||
cp_times = uv__malloc(size);
|
||||
if (cp_times == NULL) {
|
||||
uv__free(*cpu_infos);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (sysctlbyname(cptimes_key, cp_times, &size, NULL, 0)) {
|
||||
uv__free(cp_times);
|
||||
uv__free(*cpu_infos);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
for (i = 0; i < numcpus; i++) {
|
||||
cpu_info = &(*cpu_infos)[i];
|
||||
|
||||
cpu_info->cpu_times.user = (uint64_t)(cp_times[CP_USER+cur]) * multiplier;
|
||||
cpu_info->cpu_times.nice = (uint64_t)(cp_times[CP_NICE+cur]) * multiplier;
|
||||
cpu_info->cpu_times.sys = (uint64_t)(cp_times[CP_SYS+cur]) * multiplier;
|
||||
cpu_info->cpu_times.idle = (uint64_t)(cp_times[CP_IDLE+cur]) * multiplier;
|
||||
cpu_info->cpu_times.irq = (uint64_t)(cp_times[CP_INTR+cur]) * multiplier;
|
||||
|
||||
cpu_info->model = uv__strdup(model);
|
||||
cpu_info->speed = cpuspeed;
|
||||
|
||||
cur+=CPUSTATES;
|
||||
}
|
||||
|
||||
uv__free(cp_times);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
uv__free(cpu_infos[i].model);
|
||||
}
|
||||
|
||||
uv__free(cpu_infos);
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,901 @@
|
||||
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "uv.h"
|
||||
#include "internal.h"
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
|
||||
/* iOS (currently) doesn't provide the FSEvents-API (nor CoreServices) */
|
||||
|
||||
int uv__fsevents_init(uv_fs_event_t* handle) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv__fsevents_close(uv_fs_event_t* handle) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void uv__fsevents_loop_delete(uv_loop_t* loop) {
|
||||
}
|
||||
|
||||
#else /* TARGET_OS_IPHONE */
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include <CoreFoundation/CFRunLoop.h>
|
||||
#include <CoreServices/CoreServices.h>
|
||||
|
||||
/* These are macros to avoid "initializer element is not constant" errors
|
||||
* with old versions of gcc.
|
||||
*/
|
||||
#define kFSEventsModified (kFSEventStreamEventFlagItemFinderInfoMod | \
|
||||
kFSEventStreamEventFlagItemModified | \
|
||||
kFSEventStreamEventFlagItemInodeMetaMod | \
|
||||
kFSEventStreamEventFlagItemChangeOwner | \
|
||||
kFSEventStreamEventFlagItemXattrMod)
|
||||
|
||||
#define kFSEventsRenamed (kFSEventStreamEventFlagItemCreated | \
|
||||
kFSEventStreamEventFlagItemRemoved | \
|
||||
kFSEventStreamEventFlagItemRenamed)
|
||||
|
||||
#define kFSEventsSystem (kFSEventStreamEventFlagUserDropped | \
|
||||
kFSEventStreamEventFlagKernelDropped | \
|
||||
kFSEventStreamEventFlagEventIdsWrapped | \
|
||||
kFSEventStreamEventFlagHistoryDone | \
|
||||
kFSEventStreamEventFlagMount | \
|
||||
kFSEventStreamEventFlagUnmount | \
|
||||
kFSEventStreamEventFlagRootChanged)
|
||||
|
||||
typedef struct uv__fsevents_event_s uv__fsevents_event_t;
|
||||
typedef struct uv__cf_loop_signal_s uv__cf_loop_signal_t;
|
||||
typedef struct uv__cf_loop_state_s uv__cf_loop_state_t;
|
||||
|
||||
enum uv__cf_loop_signal_type_e {
|
||||
kUVCFLoopSignalRegular,
|
||||
kUVCFLoopSignalClosing
|
||||
};
|
||||
typedef enum uv__cf_loop_signal_type_e uv__cf_loop_signal_type_t;
|
||||
|
||||
struct uv__cf_loop_signal_s {
|
||||
QUEUE member;
|
||||
uv_fs_event_t* handle;
|
||||
uv__cf_loop_signal_type_t type;
|
||||
};
|
||||
|
||||
struct uv__fsevents_event_s {
|
||||
QUEUE member;
|
||||
int events;
|
||||
char path[1];
|
||||
};
|
||||
|
||||
struct uv__cf_loop_state_s {
|
||||
CFRunLoopRef loop;
|
||||
CFRunLoopSourceRef signal_source;
|
||||
int fsevent_need_reschedule;
|
||||
FSEventStreamRef fsevent_stream;
|
||||
uv_sem_t fsevent_sem;
|
||||
uv_mutex_t fsevent_mutex;
|
||||
void* fsevent_handles[2];
|
||||
unsigned int fsevent_handle_count;
|
||||
};
|
||||
|
||||
/* Forward declarations */
|
||||
static void uv__cf_loop_cb(void* arg);
|
||||
static void* uv__cf_loop_runner(void* arg);
|
||||
static int uv__cf_loop_signal(uv_loop_t* loop,
|
||||
uv_fs_event_t* handle,
|
||||
uv__cf_loop_signal_type_t type);
|
||||
|
||||
/* Lazy-loaded by uv__fsevents_global_init(). */
|
||||
static CFArrayRef (*pCFArrayCreate)(CFAllocatorRef,
|
||||
const void**,
|
||||
CFIndex,
|
||||
const CFArrayCallBacks*);
|
||||
static void (*pCFRelease)(CFTypeRef);
|
||||
static void (*pCFRunLoopAddSource)(CFRunLoopRef,
|
||||
CFRunLoopSourceRef,
|
||||
CFStringRef);
|
||||
static CFRunLoopRef (*pCFRunLoopGetCurrent)(void);
|
||||
static void (*pCFRunLoopRemoveSource)(CFRunLoopRef,
|
||||
CFRunLoopSourceRef,
|
||||
CFStringRef);
|
||||
static void (*pCFRunLoopRun)(void);
|
||||
static CFRunLoopSourceRef (*pCFRunLoopSourceCreate)(CFAllocatorRef,
|
||||
CFIndex,
|
||||
CFRunLoopSourceContext*);
|
||||
static void (*pCFRunLoopSourceSignal)(CFRunLoopSourceRef);
|
||||
static void (*pCFRunLoopStop)(CFRunLoopRef);
|
||||
static void (*pCFRunLoopWakeUp)(CFRunLoopRef);
|
||||
static CFStringRef (*pCFStringCreateWithFileSystemRepresentation)(
|
||||
CFAllocatorRef,
|
||||
const char*);
|
||||
static CFStringEncoding (*pCFStringGetSystemEncoding)(void);
|
||||
static CFStringRef (*pkCFRunLoopDefaultMode);
|
||||
static FSEventStreamRef (*pFSEventStreamCreate)(CFAllocatorRef,
|
||||
FSEventStreamCallback,
|
||||
FSEventStreamContext*,
|
||||
CFArrayRef,
|
||||
FSEventStreamEventId,
|
||||
CFTimeInterval,
|
||||
FSEventStreamCreateFlags);
|
||||
static void (*pFSEventStreamFlushSync)(FSEventStreamRef);
|
||||
static void (*pFSEventStreamInvalidate)(FSEventStreamRef);
|
||||
static void (*pFSEventStreamRelease)(FSEventStreamRef);
|
||||
static void (*pFSEventStreamScheduleWithRunLoop)(FSEventStreamRef,
|
||||
CFRunLoopRef,
|
||||
CFStringRef);
|
||||
static Boolean (*pFSEventStreamStart)(FSEventStreamRef);
|
||||
static void (*pFSEventStreamStop)(FSEventStreamRef);
|
||||
|
||||
#define UV__FSEVENTS_PROCESS(handle, block) \
|
||||
do { \
|
||||
QUEUE events; \
|
||||
QUEUE* q; \
|
||||
uv__fsevents_event_t* event; \
|
||||
int err; \
|
||||
uv_mutex_lock(&(handle)->cf_mutex); \
|
||||
/* Split-off all events and empty original queue */ \
|
||||
QUEUE_MOVE(&(handle)->cf_events, &events); \
|
||||
/* Get error (if any) and zero original one */ \
|
||||
err = (handle)->cf_error; \
|
||||
(handle)->cf_error = 0; \
|
||||
uv_mutex_unlock(&(handle)->cf_mutex); \
|
||||
/* Loop through events, deallocating each after processing */ \
|
||||
while (!QUEUE_EMPTY(&events)) { \
|
||||
q = QUEUE_HEAD(&events); \
|
||||
event = QUEUE_DATA(q, uv__fsevents_event_t, member); \
|
||||
QUEUE_REMOVE(q); \
|
||||
/* NOTE: Checking uv__is_active() is required here, because handle \
|
||||
* callback may close handle and invoking it after it will lead to \
|
||||
* incorrect behaviour */ \
|
||||
if (!uv__is_closing((handle)) && uv__is_active((handle))) \
|
||||
block \
|
||||
/* Free allocated data */ \
|
||||
uv__free(event); \
|
||||
} \
|
||||
if (err != 0 && !uv__is_closing((handle)) && uv__is_active((handle))) \
|
||||
(handle)->cb((handle), NULL, 0, err); \
|
||||
} while (0)
|
||||
|
||||
|
||||
/* Runs in UV loop's thread, when there're events to report to handle */
|
||||
static void uv__fsevents_cb(uv_async_t* cb) {
|
||||
uv_fs_event_t* handle;
|
||||
|
||||
handle = cb->data;
|
||||
|
||||
UV__FSEVENTS_PROCESS(handle, {
|
||||
handle->cb(handle, event->path[0] ? event->path : NULL, event->events, 0);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/* Runs in CF thread, pushed event into handle's event list */
|
||||
static void uv__fsevents_push_event(uv_fs_event_t* handle,
|
||||
QUEUE* events,
|
||||
int err) {
|
||||
assert(events != NULL || err != 0);
|
||||
uv_mutex_lock(&handle->cf_mutex);
|
||||
|
||||
/* Concatenate two queues */
|
||||
if (events != NULL)
|
||||
QUEUE_ADD(&handle->cf_events, events);
|
||||
|
||||
/* Propagate error */
|
||||
if (err != 0)
|
||||
handle->cf_error = err;
|
||||
uv_mutex_unlock(&handle->cf_mutex);
|
||||
|
||||
uv_async_send(handle->cf_cb);
|
||||
}
|
||||
|
||||
|
||||
/* Runs in CF thread, when there're events in FSEventStream */
|
||||
static void uv__fsevents_event_cb(ConstFSEventStreamRef streamRef,
|
||||
void* info,
|
||||
size_t numEvents,
|
||||
void* eventPaths,
|
||||
const FSEventStreamEventFlags eventFlags[],
|
||||
const FSEventStreamEventId eventIds[]) {
|
||||
size_t i;
|
||||
int len;
|
||||
char** paths;
|
||||
char* path;
|
||||
char* pos;
|
||||
uv_fs_event_t* handle;
|
||||
QUEUE* q;
|
||||
uv_loop_t* loop;
|
||||
uv__cf_loop_state_t* state;
|
||||
uv__fsevents_event_t* event;
|
||||
QUEUE head;
|
||||
|
||||
loop = info;
|
||||
state = loop->cf_state;
|
||||
assert(state != NULL);
|
||||
paths = eventPaths;
|
||||
|
||||
/* For each handle */
|
||||
uv_mutex_lock(&state->fsevent_mutex);
|
||||
QUEUE_FOREACH(q, &state->fsevent_handles) {
|
||||
handle = QUEUE_DATA(q, uv_fs_event_t, cf_member);
|
||||
QUEUE_INIT(&head);
|
||||
|
||||
/* Process and filter out events */
|
||||
for (i = 0; i < numEvents; i++) {
|
||||
/* Ignore system events */
|
||||
if (eventFlags[i] & kFSEventsSystem)
|
||||
continue;
|
||||
|
||||
path = paths[i];
|
||||
len = strlen(path);
|
||||
|
||||
/* Filter out paths that are outside handle's request */
|
||||
if (strncmp(path, handle->realpath, handle->realpath_len) != 0)
|
||||
continue;
|
||||
|
||||
if (handle->realpath_len > 1 || *handle->realpath != '/') {
|
||||
path += handle->realpath_len;
|
||||
len -= handle->realpath_len;
|
||||
|
||||
/* Skip forward slash */
|
||||
if (*path != '\0') {
|
||||
path++;
|
||||
len--;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef MAC_OS_X_VERSION_10_7
|
||||
/* Ignore events with path equal to directory itself */
|
||||
if (len == 0)
|
||||
continue;
|
||||
#endif /* MAC_OS_X_VERSION_10_7 */
|
||||
|
||||
/* Do not emit events from subdirectories (without option set) */
|
||||
if ((handle->cf_flags & UV_FS_EVENT_RECURSIVE) == 0 && *path != 0) {
|
||||
pos = strchr(path + 1, '/');
|
||||
if (pos != NULL)
|
||||
continue;
|
||||
}
|
||||
|
||||
#ifndef MAC_OS_X_VERSION_10_7
|
||||
path = "";
|
||||
len = 0;
|
||||
#endif /* MAC_OS_X_VERSION_10_7 */
|
||||
|
||||
event = uv__malloc(sizeof(*event) + len);
|
||||
if (event == NULL)
|
||||
break;
|
||||
|
||||
memset(event, 0, sizeof(*event));
|
||||
memcpy(event->path, path, len + 1);
|
||||
|
||||
if ((eventFlags[i] & kFSEventsModified) != 0 &&
|
||||
(eventFlags[i] & kFSEventsRenamed) == 0)
|
||||
event->events = UV_CHANGE;
|
||||
else
|
||||
event->events = UV_RENAME;
|
||||
|
||||
QUEUE_INSERT_TAIL(&head, &event->member);
|
||||
}
|
||||
|
||||
if (!QUEUE_EMPTY(&head))
|
||||
uv__fsevents_push_event(handle, &head, 0);
|
||||
}
|
||||
uv_mutex_unlock(&state->fsevent_mutex);
|
||||
}
|
||||
|
||||
|
||||
/* Runs in CF thread */
|
||||
static int uv__fsevents_create_stream(uv_loop_t* loop, CFArrayRef paths) {
|
||||
uv__cf_loop_state_t* state;
|
||||
FSEventStreamContext ctx;
|
||||
FSEventStreamRef ref;
|
||||
CFAbsoluteTime latency;
|
||||
FSEventStreamCreateFlags flags;
|
||||
|
||||
/* Initialize context */
|
||||
ctx.version = 0;
|
||||
ctx.info = loop;
|
||||
ctx.retain = NULL;
|
||||
ctx.release = NULL;
|
||||
ctx.copyDescription = NULL;
|
||||
|
||||
latency = 0.05;
|
||||
|
||||
/* Explanation of selected flags:
|
||||
* 1. NoDefer - without this flag, events that are happening continuously
|
||||
* (i.e. each event is happening after time interval less than `latency`,
|
||||
* counted from previous event), will be deferred and passed to callback
|
||||
* once they'll either fill whole OS buffer, or when this continuous stream
|
||||
* will stop (i.e. there'll be delay between events, bigger than
|
||||
* `latency`).
|
||||
* Specifying this flag will invoke callback after `latency` time passed
|
||||
* since event.
|
||||
* 2. FileEvents - fire callback for file changes too (by default it is firing
|
||||
* it only for directory changes).
|
||||
*/
|
||||
flags = kFSEventStreamCreateFlagNoDefer | kFSEventStreamCreateFlagFileEvents;
|
||||
|
||||
/*
|
||||
* NOTE: It might sound like a good idea to remember last seen StreamEventId,
|
||||
* but in reality one dir might have last StreamEventId less than, the other,
|
||||
* that is being watched now. Which will cause FSEventStream API to report
|
||||
* changes to files from the past.
|
||||
*/
|
||||
ref = pFSEventStreamCreate(NULL,
|
||||
&uv__fsevents_event_cb,
|
||||
&ctx,
|
||||
paths,
|
||||
kFSEventStreamEventIdSinceNow,
|
||||
latency,
|
||||
flags);
|
||||
assert(ref != NULL);
|
||||
|
||||
state = loop->cf_state;
|
||||
pFSEventStreamScheduleWithRunLoop(ref,
|
||||
state->loop,
|
||||
*pkCFRunLoopDefaultMode);
|
||||
if (!pFSEventStreamStart(ref)) {
|
||||
pFSEventStreamInvalidate(ref);
|
||||
pFSEventStreamRelease(ref);
|
||||
return -EMFILE;
|
||||
}
|
||||
|
||||
state->fsevent_stream = ref;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Runs in CF thread */
|
||||
static void uv__fsevents_destroy_stream(uv_loop_t* loop) {
|
||||
uv__cf_loop_state_t* state;
|
||||
|
||||
state = loop->cf_state;
|
||||
|
||||
if (state->fsevent_stream == NULL)
|
||||
return;
|
||||
|
||||
/* Stop emitting events */
|
||||
pFSEventStreamStop(state->fsevent_stream);
|
||||
|
||||
/* Release stream */
|
||||
pFSEventStreamInvalidate(state->fsevent_stream);
|
||||
pFSEventStreamRelease(state->fsevent_stream);
|
||||
state->fsevent_stream = NULL;
|
||||
}
|
||||
|
||||
|
||||
/* Runs in CF thread, when there're new fsevent handles to add to stream */
|
||||
static void uv__fsevents_reschedule(uv_fs_event_t* handle,
|
||||
uv__cf_loop_signal_type_t type) {
|
||||
uv__cf_loop_state_t* state;
|
||||
QUEUE* q;
|
||||
uv_fs_event_t* curr;
|
||||
CFArrayRef cf_paths;
|
||||
CFStringRef* paths;
|
||||
unsigned int i;
|
||||
int err;
|
||||
unsigned int path_count;
|
||||
|
||||
state = handle->loop->cf_state;
|
||||
paths = NULL;
|
||||
cf_paths = NULL;
|
||||
err = 0;
|
||||
/* NOTE: `i` is used in deallocation loop below */
|
||||
i = 0;
|
||||
|
||||
/* Optimization to prevent O(n^2) time spent when starting to watch
|
||||
* many files simultaneously
|
||||
*/
|
||||
uv_mutex_lock(&state->fsevent_mutex);
|
||||
if (state->fsevent_need_reschedule == 0) {
|
||||
uv_mutex_unlock(&state->fsevent_mutex);
|
||||
goto final;
|
||||
}
|
||||
state->fsevent_need_reschedule = 0;
|
||||
uv_mutex_unlock(&state->fsevent_mutex);
|
||||
|
||||
/* Destroy previous FSEventStream */
|
||||
uv__fsevents_destroy_stream(handle->loop);
|
||||
|
||||
/* Any failure below will be a memory failure */
|
||||
err = -ENOMEM;
|
||||
|
||||
/* Create list of all watched paths */
|
||||
uv_mutex_lock(&state->fsevent_mutex);
|
||||
path_count = state->fsevent_handle_count;
|
||||
if (path_count != 0) {
|
||||
paths = uv__malloc(sizeof(*paths) * path_count);
|
||||
if (paths == NULL) {
|
||||
uv_mutex_unlock(&state->fsevent_mutex);
|
||||
goto final;
|
||||
}
|
||||
|
||||
q = &state->fsevent_handles;
|
||||
for (; i < path_count; i++) {
|
||||
q = QUEUE_NEXT(q);
|
||||
assert(q != &state->fsevent_handles);
|
||||
curr = QUEUE_DATA(q, uv_fs_event_t, cf_member);
|
||||
|
||||
assert(curr->realpath != NULL);
|
||||
paths[i] =
|
||||
pCFStringCreateWithFileSystemRepresentation(NULL, curr->realpath);
|
||||
if (paths[i] == NULL) {
|
||||
uv_mutex_unlock(&state->fsevent_mutex);
|
||||
goto final;
|
||||
}
|
||||
}
|
||||
}
|
||||
uv_mutex_unlock(&state->fsevent_mutex);
|
||||
err = 0;
|
||||
|
||||
if (path_count != 0) {
|
||||
/* Create new FSEventStream */
|
||||
cf_paths = pCFArrayCreate(NULL, (const void**) paths, path_count, NULL);
|
||||
if (cf_paths == NULL) {
|
||||
err = -ENOMEM;
|
||||
goto final;
|
||||
}
|
||||
err = uv__fsevents_create_stream(handle->loop, cf_paths);
|
||||
}
|
||||
|
||||
final:
|
||||
/* Deallocate all paths in case of failure */
|
||||
if (err != 0) {
|
||||
if (cf_paths == NULL) {
|
||||
while (i != 0)
|
||||
pCFRelease(paths[--i]);
|
||||
uv__free(paths);
|
||||
} else {
|
||||
/* CFArray takes ownership of both strings and original C-array */
|
||||
pCFRelease(cf_paths);
|
||||
}
|
||||
|
||||
/* Broadcast error to all handles */
|
||||
uv_mutex_lock(&state->fsevent_mutex);
|
||||
QUEUE_FOREACH(q, &state->fsevent_handles) {
|
||||
curr = QUEUE_DATA(q, uv_fs_event_t, cf_member);
|
||||
uv__fsevents_push_event(curr, NULL, err);
|
||||
}
|
||||
uv_mutex_unlock(&state->fsevent_mutex);
|
||||
}
|
||||
|
||||
/*
|
||||
* Main thread will block until the removal of handle from the list,
|
||||
* we must tell it when we're ready.
|
||||
*
|
||||
* NOTE: This is coupled with `uv_sem_wait()` in `uv__fsevents_close`
|
||||
*/
|
||||
if (type == kUVCFLoopSignalClosing)
|
||||
uv_sem_post(&state->fsevent_sem);
|
||||
}
|
||||
|
||||
|
||||
static int uv__fsevents_global_init(void) {
|
||||
static pthread_mutex_t global_init_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
static void* core_foundation_handle;
|
||||
static void* core_services_handle;
|
||||
int err;
|
||||
|
||||
err = 0;
|
||||
pthread_mutex_lock(&global_init_mutex);
|
||||
if (core_foundation_handle != NULL)
|
||||
goto out;
|
||||
|
||||
/* The libraries are never unloaded because we currently don't have a good
|
||||
* mechanism for keeping a reference count. It's unlikely to be an issue
|
||||
* but if it ever becomes one, we can turn the dynamic library handles into
|
||||
* per-event loop properties and have the dynamic linker keep track for us.
|
||||
*/
|
||||
err = -ENOSYS;
|
||||
core_foundation_handle = dlopen("/System/Library/Frameworks/"
|
||||
"CoreFoundation.framework/"
|
||||
"Versions/A/CoreFoundation",
|
||||
RTLD_LAZY | RTLD_LOCAL);
|
||||
if (core_foundation_handle == NULL)
|
||||
goto out;
|
||||
|
||||
core_services_handle = dlopen("/System/Library/Frameworks/"
|
||||
"CoreServices.framework/"
|
||||
"Versions/A/CoreServices",
|
||||
RTLD_LAZY | RTLD_LOCAL);
|
||||
if (core_services_handle == NULL)
|
||||
goto out;
|
||||
|
||||
err = -ENOENT;
|
||||
#define V(handle, symbol) \
|
||||
do { \
|
||||
*(void **)(&p ## symbol) = dlsym((handle), #symbol); \
|
||||
if (p ## symbol == NULL) \
|
||||
goto out; \
|
||||
} \
|
||||
while (0)
|
||||
V(core_foundation_handle, CFArrayCreate);
|
||||
V(core_foundation_handle, CFRelease);
|
||||
V(core_foundation_handle, CFRunLoopAddSource);
|
||||
V(core_foundation_handle, CFRunLoopGetCurrent);
|
||||
V(core_foundation_handle, CFRunLoopRemoveSource);
|
||||
V(core_foundation_handle, CFRunLoopRun);
|
||||
V(core_foundation_handle, CFRunLoopSourceCreate);
|
||||
V(core_foundation_handle, CFRunLoopSourceSignal);
|
||||
V(core_foundation_handle, CFRunLoopStop);
|
||||
V(core_foundation_handle, CFRunLoopWakeUp);
|
||||
V(core_foundation_handle, CFStringCreateWithFileSystemRepresentation);
|
||||
V(core_foundation_handle, CFStringGetSystemEncoding);
|
||||
V(core_foundation_handle, kCFRunLoopDefaultMode);
|
||||
V(core_services_handle, FSEventStreamCreate);
|
||||
V(core_services_handle, FSEventStreamFlushSync);
|
||||
V(core_services_handle, FSEventStreamInvalidate);
|
||||
V(core_services_handle, FSEventStreamRelease);
|
||||
V(core_services_handle, FSEventStreamScheduleWithRunLoop);
|
||||
V(core_services_handle, FSEventStreamStart);
|
||||
V(core_services_handle, FSEventStreamStop);
|
||||
#undef V
|
||||
err = 0;
|
||||
|
||||
out:
|
||||
if (err && core_services_handle != NULL) {
|
||||
dlclose(core_services_handle);
|
||||
core_services_handle = NULL;
|
||||
}
|
||||
|
||||
if (err && core_foundation_handle != NULL) {
|
||||
dlclose(core_foundation_handle);
|
||||
core_foundation_handle = NULL;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&global_init_mutex);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
/* Runs in UV loop */
|
||||
static int uv__fsevents_loop_init(uv_loop_t* loop) {
|
||||
CFRunLoopSourceContext ctx;
|
||||
uv__cf_loop_state_t* state;
|
||||
pthread_attr_t attr_storage;
|
||||
pthread_attr_t* attr;
|
||||
int err;
|
||||
|
||||
if (loop->cf_state != NULL)
|
||||
return 0;
|
||||
|
||||
err = uv__fsevents_global_init();
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
state = uv__calloc(1, sizeof(*state));
|
||||
if (state == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
err = uv_mutex_init(&loop->cf_mutex);
|
||||
if (err)
|
||||
goto fail_mutex_init;
|
||||
|
||||
err = uv_sem_init(&loop->cf_sem, 0);
|
||||
if (err)
|
||||
goto fail_sem_init;
|
||||
|
||||
QUEUE_INIT(&loop->cf_signals);
|
||||
|
||||
err = uv_sem_init(&state->fsevent_sem, 0);
|
||||
if (err)
|
||||
goto fail_fsevent_sem_init;
|
||||
|
||||
err = uv_mutex_init(&state->fsevent_mutex);
|
||||
if (err)
|
||||
goto fail_fsevent_mutex_init;
|
||||
|
||||
QUEUE_INIT(&state->fsevent_handles);
|
||||
state->fsevent_need_reschedule = 0;
|
||||
state->fsevent_handle_count = 0;
|
||||
|
||||
memset(&ctx, 0, sizeof(ctx));
|
||||
ctx.info = loop;
|
||||
ctx.perform = uv__cf_loop_cb;
|
||||
state->signal_source = pCFRunLoopSourceCreate(NULL, 0, &ctx);
|
||||
if (state->signal_source == NULL) {
|
||||
err = -ENOMEM;
|
||||
goto fail_signal_source_create;
|
||||
}
|
||||
|
||||
/* In the unlikely event that pthread_attr_init() fails, create the thread
|
||||
* with the default stack size. We'll use a little more address space but
|
||||
* that in itself is not a fatal error.
|
||||
*/
|
||||
attr = &attr_storage;
|
||||
if (pthread_attr_init(attr))
|
||||
attr = NULL;
|
||||
|
||||
if (attr != NULL)
|
||||
if (pthread_attr_setstacksize(attr, 4 * PTHREAD_STACK_MIN))
|
||||
abort();
|
||||
|
||||
loop->cf_state = state;
|
||||
|
||||
/* uv_thread_t is an alias for pthread_t. */
|
||||
err = -pthread_create(&loop->cf_thread, attr, uv__cf_loop_runner, loop);
|
||||
|
||||
if (attr != NULL)
|
||||
pthread_attr_destroy(attr);
|
||||
|
||||
if (err)
|
||||
goto fail_thread_create;
|
||||
|
||||
/* Synchronize threads */
|
||||
uv_sem_wait(&loop->cf_sem);
|
||||
return 0;
|
||||
|
||||
fail_thread_create:
|
||||
loop->cf_state = NULL;
|
||||
|
||||
fail_signal_source_create:
|
||||
uv_mutex_destroy(&state->fsevent_mutex);
|
||||
|
||||
fail_fsevent_mutex_init:
|
||||
uv_sem_destroy(&state->fsevent_sem);
|
||||
|
||||
fail_fsevent_sem_init:
|
||||
uv_sem_destroy(&loop->cf_sem);
|
||||
|
||||
fail_sem_init:
|
||||
uv_mutex_destroy(&loop->cf_mutex);
|
||||
|
||||
fail_mutex_init:
|
||||
uv__free(state);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
/* Runs in UV loop */
|
||||
void uv__fsevents_loop_delete(uv_loop_t* loop) {
|
||||
uv__cf_loop_signal_t* s;
|
||||
uv__cf_loop_state_t* state;
|
||||
QUEUE* q;
|
||||
|
||||
if (loop->cf_state == NULL)
|
||||
return;
|
||||
|
||||
if (uv__cf_loop_signal(loop, NULL, kUVCFLoopSignalRegular) != 0)
|
||||
abort();
|
||||
|
||||
uv_thread_join(&loop->cf_thread);
|
||||
uv_sem_destroy(&loop->cf_sem);
|
||||
uv_mutex_destroy(&loop->cf_mutex);
|
||||
|
||||
/* Free any remaining data */
|
||||
while (!QUEUE_EMPTY(&loop->cf_signals)) {
|
||||
q = QUEUE_HEAD(&loop->cf_signals);
|
||||
s = QUEUE_DATA(q, uv__cf_loop_signal_t, member);
|
||||
QUEUE_REMOVE(q);
|
||||
uv__free(s);
|
||||
}
|
||||
|
||||
/* Destroy state */
|
||||
state = loop->cf_state;
|
||||
uv_sem_destroy(&state->fsevent_sem);
|
||||
uv_mutex_destroy(&state->fsevent_mutex);
|
||||
pCFRelease(state->signal_source);
|
||||
uv__free(state);
|
||||
loop->cf_state = NULL;
|
||||
}
|
||||
|
||||
|
||||
/* Runs in CF thread. This is the CF loop's body */
|
||||
static void* uv__cf_loop_runner(void* arg) {
|
||||
uv_loop_t* loop;
|
||||
uv__cf_loop_state_t* state;
|
||||
|
||||
loop = arg;
|
||||
state = loop->cf_state;
|
||||
state->loop = pCFRunLoopGetCurrent();
|
||||
|
||||
pCFRunLoopAddSource(state->loop,
|
||||
state->signal_source,
|
||||
*pkCFRunLoopDefaultMode);
|
||||
|
||||
uv_sem_post(&loop->cf_sem);
|
||||
|
||||
pCFRunLoopRun();
|
||||
pCFRunLoopRemoveSource(state->loop,
|
||||
state->signal_source,
|
||||
*pkCFRunLoopDefaultMode);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/* Runs in CF thread, executed after `uv__cf_loop_signal()` */
|
||||
static void uv__cf_loop_cb(void* arg) {
|
||||
uv_loop_t* loop;
|
||||
uv__cf_loop_state_t* state;
|
||||
QUEUE* item;
|
||||
QUEUE split_head;
|
||||
uv__cf_loop_signal_t* s;
|
||||
|
||||
loop = arg;
|
||||
state = loop->cf_state;
|
||||
|
||||
uv_mutex_lock(&loop->cf_mutex);
|
||||
QUEUE_MOVE(&loop->cf_signals, &split_head);
|
||||
uv_mutex_unlock(&loop->cf_mutex);
|
||||
|
||||
while (!QUEUE_EMPTY(&split_head)) {
|
||||
item = QUEUE_HEAD(&split_head);
|
||||
QUEUE_REMOVE(item);
|
||||
|
||||
s = QUEUE_DATA(item, uv__cf_loop_signal_t, member);
|
||||
|
||||
/* This was a termination signal */
|
||||
if (s->handle == NULL)
|
||||
pCFRunLoopStop(state->loop);
|
||||
else
|
||||
uv__fsevents_reschedule(s->handle, s->type);
|
||||
|
||||
uv__free(s);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Runs in UV loop to notify CF thread */
|
||||
int uv__cf_loop_signal(uv_loop_t* loop,
|
||||
uv_fs_event_t* handle,
|
||||
uv__cf_loop_signal_type_t type) {
|
||||
uv__cf_loop_signal_t* item;
|
||||
uv__cf_loop_state_t* state;
|
||||
|
||||
item = uv__malloc(sizeof(*item));
|
||||
if (item == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
item->handle = handle;
|
||||
item->type = type;
|
||||
|
||||
uv_mutex_lock(&loop->cf_mutex);
|
||||
QUEUE_INSERT_TAIL(&loop->cf_signals, &item->member);
|
||||
uv_mutex_unlock(&loop->cf_mutex);
|
||||
|
||||
state = loop->cf_state;
|
||||
assert(state != NULL);
|
||||
pCFRunLoopSourceSignal(state->signal_source);
|
||||
pCFRunLoopWakeUp(state->loop);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Runs in UV loop to initialize handle */
|
||||
int uv__fsevents_init(uv_fs_event_t* handle) {
|
||||
int err;
|
||||
uv__cf_loop_state_t* state;
|
||||
|
||||
err = uv__fsevents_loop_init(handle->loop);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* Get absolute path to file */
|
||||
handle->realpath = realpath(handle->path, NULL);
|
||||
if (handle->realpath == NULL)
|
||||
return -errno;
|
||||
handle->realpath_len = strlen(handle->realpath);
|
||||
|
||||
/* Initialize event queue */
|
||||
QUEUE_INIT(&handle->cf_events);
|
||||
handle->cf_error = 0;
|
||||
|
||||
/*
|
||||
* Events will occur in other thread.
|
||||
* Initialize callback for getting them back into event loop's thread
|
||||
*/
|
||||
handle->cf_cb = uv__malloc(sizeof(*handle->cf_cb));
|
||||
if (handle->cf_cb == NULL) {
|
||||
err = -ENOMEM;
|
||||
goto fail_cf_cb_malloc;
|
||||
}
|
||||
|
||||
handle->cf_cb->data = handle;
|
||||
uv_async_init(handle->loop, handle->cf_cb, uv__fsevents_cb);
|
||||
handle->cf_cb->flags |= UV__HANDLE_INTERNAL;
|
||||
uv_unref((uv_handle_t*) handle->cf_cb);
|
||||
|
||||
err = uv_mutex_init(&handle->cf_mutex);
|
||||
if (err)
|
||||
goto fail_cf_mutex_init;
|
||||
|
||||
/* Insert handle into the list */
|
||||
state = handle->loop->cf_state;
|
||||
uv_mutex_lock(&state->fsevent_mutex);
|
||||
QUEUE_INSERT_TAIL(&state->fsevent_handles, &handle->cf_member);
|
||||
state->fsevent_handle_count++;
|
||||
state->fsevent_need_reschedule = 1;
|
||||
uv_mutex_unlock(&state->fsevent_mutex);
|
||||
|
||||
/* Reschedule FSEventStream */
|
||||
assert(handle != NULL);
|
||||
err = uv__cf_loop_signal(handle->loop, handle, kUVCFLoopSignalRegular);
|
||||
if (err)
|
||||
goto fail_loop_signal;
|
||||
|
||||
return 0;
|
||||
|
||||
fail_loop_signal:
|
||||
uv_mutex_destroy(&handle->cf_mutex);
|
||||
|
||||
fail_cf_mutex_init:
|
||||
uv__free(handle->cf_cb);
|
||||
handle->cf_cb = NULL;
|
||||
|
||||
fail_cf_cb_malloc:
|
||||
uv__free(handle->realpath);
|
||||
handle->realpath = NULL;
|
||||
handle->realpath_len = 0;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
/* Runs in UV loop to de-initialize handle */
|
||||
int uv__fsevents_close(uv_fs_event_t* handle) {
|
||||
int err;
|
||||
uv__cf_loop_state_t* state;
|
||||
|
||||
if (handle->cf_cb == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
/* Remove handle from the list */
|
||||
state = handle->loop->cf_state;
|
||||
uv_mutex_lock(&state->fsevent_mutex);
|
||||
QUEUE_REMOVE(&handle->cf_member);
|
||||
state->fsevent_handle_count--;
|
||||
state->fsevent_need_reschedule = 1;
|
||||
uv_mutex_unlock(&state->fsevent_mutex);
|
||||
|
||||
/* Reschedule FSEventStream */
|
||||
assert(handle != NULL);
|
||||
err = uv__cf_loop_signal(handle->loop, handle, kUVCFLoopSignalClosing);
|
||||
if (err)
|
||||
return -err;
|
||||
|
||||
/* Wait for deinitialization */
|
||||
uv_sem_wait(&state->fsevent_sem);
|
||||
|
||||
uv_close((uv_handle_t*) handle->cf_cb, (uv_close_cb) uv__free);
|
||||
handle->cf_cb = NULL;
|
||||
|
||||
/* Free data in queue */
|
||||
UV__FSEVENTS_PROCESS(handle, {
|
||||
/* NOP */
|
||||
});
|
||||
|
||||
uv_mutex_destroy(&handle->cf_mutex);
|
||||
uv__free(handle->realpath);
|
||||
handle->realpath = NULL;
|
||||
handle->realpath_len = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* TARGET_OS_IPHONE */
|
||||
@@ -0,0 +1,202 @@
|
||||
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/* Expose glibc-specific EAI_* error codes. Needs to be defined before we
|
||||
* include any headers.
|
||||
*/
|
||||
#ifndef _GNU_SOURCE
|
||||
# define _GNU_SOURCE
|
||||
#endif
|
||||
|
||||
#include "uv.h"
|
||||
#include "internal.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <stddef.h> /* NULL */
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/* EAI_* constants. */
|
||||
#include <netdb.h>
|
||||
|
||||
|
||||
int uv__getaddrinfo_translate_error(int sys_err) {
|
||||
switch (sys_err) {
|
||||
case 0: return 0;
|
||||
#if defined(EAI_ADDRFAMILY)
|
||||
case EAI_ADDRFAMILY: return UV_EAI_ADDRFAMILY;
|
||||
#endif
|
||||
#if defined(EAI_AGAIN)
|
||||
case EAI_AGAIN: return UV_EAI_AGAIN;
|
||||
#endif
|
||||
#if defined(EAI_BADFLAGS)
|
||||
case EAI_BADFLAGS: return UV_EAI_BADFLAGS;
|
||||
#endif
|
||||
#if defined(EAI_BADHINTS)
|
||||
case EAI_BADHINTS: return UV_EAI_BADHINTS;
|
||||
#endif
|
||||
#if defined(EAI_CANCELED)
|
||||
case EAI_CANCELED: return UV_EAI_CANCELED;
|
||||
#endif
|
||||
#if defined(EAI_FAIL)
|
||||
case EAI_FAIL: return UV_EAI_FAIL;
|
||||
#endif
|
||||
#if defined(EAI_FAMILY)
|
||||
case EAI_FAMILY: return UV_EAI_FAMILY;
|
||||
#endif
|
||||
#if defined(EAI_MEMORY)
|
||||
case EAI_MEMORY: return UV_EAI_MEMORY;
|
||||
#endif
|
||||
#if defined(EAI_NODATA)
|
||||
case EAI_NODATA: return UV_EAI_NODATA;
|
||||
#endif
|
||||
#if defined(EAI_NONAME)
|
||||
# if !defined(EAI_NODATA) || EAI_NODATA != EAI_NONAME
|
||||
case EAI_NONAME: return UV_EAI_NONAME;
|
||||
# endif
|
||||
#endif
|
||||
#if defined(EAI_OVERFLOW)
|
||||
case EAI_OVERFLOW: return UV_EAI_OVERFLOW;
|
||||
#endif
|
||||
#if defined(EAI_PROTOCOL)
|
||||
case EAI_PROTOCOL: return UV_EAI_PROTOCOL;
|
||||
#endif
|
||||
#if defined(EAI_SERVICE)
|
||||
case EAI_SERVICE: return UV_EAI_SERVICE;
|
||||
#endif
|
||||
#if defined(EAI_SOCKTYPE)
|
||||
case EAI_SOCKTYPE: return UV_EAI_SOCKTYPE;
|
||||
#endif
|
||||
#if defined(EAI_SYSTEM)
|
||||
case EAI_SYSTEM: return -errno;
|
||||
#endif
|
||||
}
|
||||
assert(!"unknown EAI_* error code");
|
||||
abort();
|
||||
return 0; /* Pacify compiler. */
|
||||
}
|
||||
|
||||
|
||||
static void uv__getaddrinfo_work(struct uv__work* w) {
|
||||
uv_getaddrinfo_t* req;
|
||||
int err;
|
||||
|
||||
req = container_of(w, uv_getaddrinfo_t, work_req);
|
||||
err = getaddrinfo(req->hostname, req->service, req->hints, &req->addrinfo);
|
||||
req->retcode = uv__getaddrinfo_translate_error(err);
|
||||
}
|
||||
|
||||
|
||||
static void uv__getaddrinfo_done(struct uv__work* w, int status) {
|
||||
uv_getaddrinfo_t* req;
|
||||
|
||||
req = container_of(w, uv_getaddrinfo_t, work_req);
|
||||
uv__req_unregister(req->loop, req);
|
||||
|
||||
/* See initialization in uv_getaddrinfo(). */
|
||||
if (req->hints)
|
||||
uv__free(req->hints);
|
||||
else if (req->service)
|
||||
uv__free(req->service);
|
||||
else if (req->hostname)
|
||||
uv__free(req->hostname);
|
||||
else
|
||||
assert(0);
|
||||
|
||||
req->hints = NULL;
|
||||
req->service = NULL;
|
||||
req->hostname = NULL;
|
||||
|
||||
if (status == -ECANCELED) {
|
||||
assert(req->retcode == 0);
|
||||
req->retcode = UV_EAI_CANCELED;
|
||||
}
|
||||
|
||||
if (req->cb)
|
||||
req->cb(req, req->retcode, req->addrinfo);
|
||||
}
|
||||
|
||||
|
||||
int uv_getaddrinfo(uv_loop_t* loop,
|
||||
uv_getaddrinfo_t* req,
|
||||
uv_getaddrinfo_cb cb,
|
||||
const char* hostname,
|
||||
const char* service,
|
||||
const struct addrinfo* hints) {
|
||||
size_t hostname_len;
|
||||
size_t service_len;
|
||||
size_t hints_len;
|
||||
size_t len;
|
||||
char* buf;
|
||||
|
||||
if (req == NULL || (hostname == NULL && service == NULL))
|
||||
return -EINVAL;
|
||||
|
||||
hostname_len = hostname ? strlen(hostname) + 1 : 0;
|
||||
service_len = service ? strlen(service) + 1 : 0;
|
||||
hints_len = hints ? sizeof(*hints) : 0;
|
||||
buf = uv__malloc(hostname_len + service_len + hints_len);
|
||||
|
||||
if (buf == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
uv__req_init(loop, req, UV_GETADDRINFO);
|
||||
req->loop = loop;
|
||||
req->cb = cb;
|
||||
req->addrinfo = NULL;
|
||||
req->hints = NULL;
|
||||
req->service = NULL;
|
||||
req->hostname = NULL;
|
||||
req->retcode = 0;
|
||||
|
||||
/* order matters, see uv_getaddrinfo_done() */
|
||||
len = 0;
|
||||
|
||||
if (hints) {
|
||||
req->hints = memcpy(buf + len, hints, sizeof(*hints));
|
||||
len += sizeof(*hints);
|
||||
}
|
||||
|
||||
if (service) {
|
||||
req->service = memcpy(buf + len, service, service_len);
|
||||
len += service_len;
|
||||
}
|
||||
|
||||
if (hostname)
|
||||
req->hostname = memcpy(buf + len, hostname, hostname_len);
|
||||
|
||||
if (cb) {
|
||||
uv__work_submit(loop,
|
||||
&req->work_req,
|
||||
uv__getaddrinfo_work,
|
||||
uv__getaddrinfo_done);
|
||||
return 0;
|
||||
} else {
|
||||
uv__getaddrinfo_work(&req->work_req);
|
||||
uv__getaddrinfo_done(&req->work_req, 0);
|
||||
return req->retcode;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void uv_freeaddrinfo(struct addrinfo* ai) {
|
||||
if (ai)
|
||||
freeaddrinfo(ai);
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "uv.h"
|
||||
#include "internal.h"
|
||||
|
||||
|
||||
static void uv__getnameinfo_work(struct uv__work* w) {
|
||||
uv_getnameinfo_t* req;
|
||||
int err;
|
||||
socklen_t salen;
|
||||
|
||||
req = container_of(w, uv_getnameinfo_t, work_req);
|
||||
|
||||
if (req->storage.ss_family == AF_INET)
|
||||
salen = sizeof(struct sockaddr_in);
|
||||
else if (req->storage.ss_family == AF_INET6)
|
||||
salen = sizeof(struct sockaddr_in6);
|
||||
else
|
||||
abort();
|
||||
|
||||
err = getnameinfo((struct sockaddr*) &req->storage,
|
||||
salen,
|
||||
req->host,
|
||||
sizeof(req->host),
|
||||
req->service,
|
||||
sizeof(req->service),
|
||||
req->flags);
|
||||
req->retcode = uv__getaddrinfo_translate_error(err);
|
||||
}
|
||||
|
||||
static void uv__getnameinfo_done(struct uv__work* w, int status) {
|
||||
uv_getnameinfo_t* req;
|
||||
char* host;
|
||||
char* service;
|
||||
|
||||
req = container_of(w, uv_getnameinfo_t, work_req);
|
||||
uv__req_unregister(req->loop, req);
|
||||
host = service = NULL;
|
||||
|
||||
if (status == -ECANCELED) {
|
||||
assert(req->retcode == 0);
|
||||
req->retcode = UV_EAI_CANCELED;
|
||||
} else if (req->retcode == 0) {
|
||||
host = req->host;
|
||||
service = req->service;
|
||||
}
|
||||
|
||||
if (req->getnameinfo_cb)
|
||||
req->getnameinfo_cb(req, req->retcode, host, service);
|
||||
}
|
||||
|
||||
/*
|
||||
* Entry point for getnameinfo
|
||||
* return 0 if a callback will be made
|
||||
* return error code if validation fails
|
||||
*/
|
||||
int uv_getnameinfo(uv_loop_t* loop,
|
||||
uv_getnameinfo_t* req,
|
||||
uv_getnameinfo_cb getnameinfo_cb,
|
||||
const struct sockaddr* addr,
|
||||
int flags) {
|
||||
if (req == NULL || addr == NULL)
|
||||
return UV_EINVAL;
|
||||
|
||||
if (addr->sa_family == AF_INET) {
|
||||
memcpy(&req->storage,
|
||||
addr,
|
||||
sizeof(struct sockaddr_in));
|
||||
} else if (addr->sa_family == AF_INET6) {
|
||||
memcpy(&req->storage,
|
||||
addr,
|
||||
sizeof(struct sockaddr_in6));
|
||||
} else {
|
||||
return UV_EINVAL;
|
||||
}
|
||||
|
||||
uv__req_init(loop, (uv_req_t*)req, UV_GETNAMEINFO);
|
||||
|
||||
req->getnameinfo_cb = getnameinfo_cb;
|
||||
req->flags = flags;
|
||||
req->type = UV_GETNAMEINFO;
|
||||
req->loop = loop;
|
||||
req->retcode = 0;
|
||||
|
||||
if (getnameinfo_cb) {
|
||||
uv__work_submit(loop,
|
||||
&req->work_req,
|
||||
uv__getnameinfo_work,
|
||||
uv__getnameinfo_done);
|
||||
return 0;
|
||||
} else {
|
||||
uv__getnameinfo_work(&req->work_req);
|
||||
uv__getnameinfo_done(&req->work_req, 0);
|
||||
return req->retcode;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,338 @@
|
||||
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef UV_UNIX_INTERNAL_H_
|
||||
#define UV_UNIX_INTERNAL_H_
|
||||
|
||||
#include "uv-common.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h> /* abort */
|
||||
#include <string.h> /* strrchr */
|
||||
#include <fcntl.h> /* O_CLOEXEC, may be */
|
||||
#include <stdio.h>
|
||||
|
||||
#if defined(__STRICT_ANSI__)
|
||||
# define inline __inline
|
||||
#endif
|
||||
|
||||
#if defined(__linux__)
|
||||
# include "linux-syscalls.h"
|
||||
#endif /* __linux__ */
|
||||
|
||||
#if defined(__MVS__)
|
||||
# include "os390-syscalls.h"
|
||||
#endif /* __MVS__ */
|
||||
|
||||
#if defined(__sun)
|
||||
# include <sys/port.h>
|
||||
# include <port.h>
|
||||
#endif /* __sun */
|
||||
|
||||
#if defined(_AIX)
|
||||
# define reqevents events
|
||||
# define rtnevents revents
|
||||
# include <sys/poll.h>
|
||||
#else
|
||||
# include <poll.h>
|
||||
#endif /* _AIX */
|
||||
|
||||
#if defined(__APPLE__) && !TARGET_OS_IPHONE
|
||||
# include <AvailabilityMacros.h>
|
||||
#endif
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
int uv__pthread_sigmask(int how, const sigset_t* set, sigset_t* oset);
|
||||
# ifdef pthread_sigmask
|
||||
# undef pthread_sigmask
|
||||
# endif
|
||||
# define pthread_sigmask(how, set, oldset) uv__pthread_sigmask(how, set, oldset)
|
||||
#endif
|
||||
|
||||
#define ACCESS_ONCE(type, var) \
|
||||
(*(volatile type*) &(var))
|
||||
|
||||
#define ROUND_UP(a, b) \
|
||||
((a) % (b) ? ((a) + (b)) - ((a) % (b)) : (a))
|
||||
|
||||
#define UNREACHABLE() \
|
||||
do { \
|
||||
assert(0 && "unreachable code"); \
|
||||
abort(); \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
#define SAVE_ERRNO(block) \
|
||||
do { \
|
||||
int _saved_errno = errno; \
|
||||
do { block; } while (0); \
|
||||
errno = _saved_errno; \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
/* The __clang__ and __INTEL_COMPILER checks are superfluous because they
|
||||
* define __GNUC__. They are here to convey to you, dear reader, that these
|
||||
* macros are enabled when compiling with clang or icc.
|
||||
*/
|
||||
#if defined(__clang__) || \
|
||||
defined(__GNUC__) || \
|
||||
defined(__INTEL_COMPILER) || \
|
||||
defined(__SUNPRO_C)
|
||||
# define UV_DESTRUCTOR(declaration) __attribute__((destructor)) declaration
|
||||
# define UV_UNUSED(declaration) __attribute__((unused)) declaration
|
||||
#else
|
||||
# define UV_DESTRUCTOR(declaration) declaration
|
||||
# define UV_UNUSED(declaration) declaration
|
||||
#endif
|
||||
|
||||
/* Leans on the fact that, on Linux, POLLRDHUP == EPOLLRDHUP. */
|
||||
#ifdef POLLRDHUP
|
||||
# define UV__POLLRDHUP POLLRDHUP
|
||||
#else
|
||||
# define UV__POLLRDHUP 0x2000
|
||||
#endif
|
||||
|
||||
#ifdef POLLPRI
|
||||
# define UV__POLLPRI POLLPRI
|
||||
#else
|
||||
# define UV__POLLPRI 0
|
||||
#endif
|
||||
|
||||
#if !defined(O_CLOEXEC) && defined(__FreeBSD__)
|
||||
/*
|
||||
* It may be that we are just missing `__POSIX_VISIBLE >= 200809`.
|
||||
* Try using fixed value const and give up, if it doesn't work
|
||||
*/
|
||||
# define O_CLOEXEC 0x00100000
|
||||
#endif
|
||||
|
||||
typedef struct uv__stream_queued_fds_s uv__stream_queued_fds_t;
|
||||
|
||||
/* handle flags */
|
||||
enum {
|
||||
UV_CLOSING = 0x01, /* uv_close() called but not finished. */
|
||||
UV_CLOSED = 0x02, /* close(2) finished. */
|
||||
UV_STREAM_READING = 0x04, /* uv_read_start() called. */
|
||||
UV_STREAM_SHUTTING = 0x08, /* uv_shutdown() called but not complete. */
|
||||
UV_STREAM_SHUT = 0x10, /* Write side closed. */
|
||||
UV_STREAM_READABLE = 0x20, /* The stream is readable */
|
||||
UV_STREAM_WRITABLE = 0x40, /* The stream is writable */
|
||||
UV_STREAM_BLOCKING = 0x80, /* Synchronous writes. */
|
||||
UV_STREAM_READ_PARTIAL = 0x100, /* read(2) read less than requested. */
|
||||
UV_STREAM_READ_EOF = 0x200, /* read(2) read EOF. */
|
||||
UV_TCP_NODELAY = 0x400, /* Disable Nagle. */
|
||||
UV_TCP_KEEPALIVE = 0x800, /* Turn on keep-alive. */
|
||||
UV_TCP_SINGLE_ACCEPT = 0x1000, /* Only accept() when idle. */
|
||||
UV_HANDLE_IPV6 = 0x10000, /* Handle is bound to a IPv6 socket. */
|
||||
UV_UDP_PROCESSING = 0x20000, /* Handle is running the send callback queue. */
|
||||
UV_HANDLE_BOUND = 0x40000 /* Handle is bound to an address and port */
|
||||
};
|
||||
|
||||
/* loop flags */
|
||||
enum {
|
||||
UV_LOOP_BLOCK_SIGPROF = 1
|
||||
};
|
||||
|
||||
/* flags of excluding ifaddr */
|
||||
enum {
|
||||
UV__EXCLUDE_IFPHYS,
|
||||
UV__EXCLUDE_IFADDR
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
UV_CLOCK_PRECISE = 0, /* Use the highest resolution clock available. */
|
||||
UV_CLOCK_FAST = 1 /* Use the fastest clock with <= 1ms granularity. */
|
||||
} uv_clocktype_t;
|
||||
|
||||
struct uv__stream_queued_fds_s {
|
||||
unsigned int size;
|
||||
unsigned int offset;
|
||||
int fds[1];
|
||||
};
|
||||
|
||||
|
||||
#if defined(_AIX) || \
|
||||
defined(__APPLE__) || \
|
||||
defined(__DragonFly__) || \
|
||||
defined(__FreeBSD__) || \
|
||||
defined(__FreeBSD_kernel__) || \
|
||||
defined(__linux__) || \
|
||||
defined(__OpenBSD__)
|
||||
#define uv__cloexec uv__cloexec_ioctl
|
||||
#define uv__nonblock uv__nonblock_ioctl
|
||||
#else
|
||||
#define uv__cloexec uv__cloexec_fcntl
|
||||
#define uv__nonblock uv__nonblock_fcntl
|
||||
#endif
|
||||
|
||||
/* core */
|
||||
int uv__cloexec_ioctl(int fd, int set);
|
||||
int uv__cloexec_fcntl(int fd, int set);
|
||||
int uv__nonblock_ioctl(int fd, int set);
|
||||
int uv__nonblock_fcntl(int fd, int set);
|
||||
int uv__close(int fd);
|
||||
int uv__close_nocheckstdio(int fd);
|
||||
int uv__socket(int domain, int type, int protocol);
|
||||
int uv__dup(int fd);
|
||||
ssize_t uv__recvmsg(int fd, struct msghdr *msg, int flags);
|
||||
void uv__make_close_pending(uv_handle_t* handle);
|
||||
int uv__getiovmax(void);
|
||||
|
||||
void uv__io_init(uv__io_t* w, uv__io_cb cb, int fd);
|
||||
void uv__io_start(uv_loop_t* loop, uv__io_t* w, unsigned int events);
|
||||
void uv__io_stop(uv_loop_t* loop, uv__io_t* w, unsigned int events);
|
||||
void uv__io_close(uv_loop_t* loop, uv__io_t* w);
|
||||
void uv__io_feed(uv_loop_t* loop, uv__io_t* w);
|
||||
int uv__io_active(const uv__io_t* w, unsigned int events);
|
||||
int uv__io_check_fd(uv_loop_t* loop, int fd);
|
||||
void uv__io_poll(uv_loop_t* loop, int timeout); /* in milliseconds or -1 */
|
||||
int uv__io_fork(uv_loop_t* loop);
|
||||
|
||||
/* async */
|
||||
void uv__async_stop(uv_loop_t* loop);
|
||||
int uv__async_fork(uv_loop_t* loop);
|
||||
|
||||
|
||||
/* loop */
|
||||
void uv__run_idle(uv_loop_t* loop);
|
||||
void uv__run_check(uv_loop_t* loop);
|
||||
void uv__run_prepare(uv_loop_t* loop);
|
||||
|
||||
/* stream */
|
||||
void uv__stream_init(uv_loop_t* loop, uv_stream_t* stream,
|
||||
uv_handle_type type);
|
||||
int uv__stream_open(uv_stream_t*, int fd, int flags);
|
||||
void uv__stream_destroy(uv_stream_t* stream);
|
||||
#if defined(__APPLE__)
|
||||
int uv__stream_try_select(uv_stream_t* stream, int* fd);
|
||||
#endif /* defined(__APPLE__) */
|
||||
void uv__server_io(uv_loop_t* loop, uv__io_t* w, unsigned int events);
|
||||
int uv__accept(int sockfd);
|
||||
int uv__dup2_cloexec(int oldfd, int newfd);
|
||||
int uv__open_cloexec(const char* path, int flags);
|
||||
|
||||
/* tcp */
|
||||
int uv_tcp_listen(uv_tcp_t* tcp, int backlog, uv_connection_cb cb);
|
||||
int uv__tcp_nodelay(int fd, int on);
|
||||
int uv__tcp_keepalive(int fd, int on, unsigned int delay);
|
||||
|
||||
/* pipe */
|
||||
int uv_pipe_listen(uv_pipe_t* handle, int backlog, uv_connection_cb cb);
|
||||
|
||||
/* timer */
|
||||
void uv__run_timers(uv_loop_t* loop);
|
||||
int uv__next_timeout(const uv_loop_t* loop);
|
||||
|
||||
/* signal */
|
||||
void uv__signal_close(uv_signal_t* handle);
|
||||
void uv__signal_global_once_init(void);
|
||||
void uv__signal_loop_cleanup(uv_loop_t* loop);
|
||||
int uv__signal_loop_fork(uv_loop_t* loop);
|
||||
|
||||
/* platform specific */
|
||||
uint64_t uv__hrtime(uv_clocktype_t type);
|
||||
int uv__kqueue_init(uv_loop_t* loop);
|
||||
int uv__platform_loop_init(uv_loop_t* loop);
|
||||
void uv__platform_loop_delete(uv_loop_t* loop);
|
||||
void uv__platform_invalidate_fd(uv_loop_t* loop, int fd);
|
||||
|
||||
/* various */
|
||||
void uv__async_close(uv_async_t* handle);
|
||||
void uv__check_close(uv_check_t* handle);
|
||||
void uv__fs_event_close(uv_fs_event_t* handle);
|
||||
void uv__idle_close(uv_idle_t* handle);
|
||||
void uv__pipe_close(uv_pipe_t* handle);
|
||||
void uv__poll_close(uv_poll_t* handle);
|
||||
void uv__prepare_close(uv_prepare_t* handle);
|
||||
void uv__process_close(uv_process_t* handle);
|
||||
void uv__stream_close(uv_stream_t* handle);
|
||||
void uv__tcp_close(uv_tcp_t* handle);
|
||||
void uv__timer_close(uv_timer_t* handle);
|
||||
void uv__udp_close(uv_udp_t* handle);
|
||||
void uv__udp_finish_close(uv_udp_t* handle);
|
||||
uv_handle_type uv__handle_type(int fd);
|
||||
FILE* uv__open_file(const char* path);
|
||||
int uv__getpwuid_r(uv_passwd_t* pwd);
|
||||
|
||||
|
||||
#if defined(__APPLE__)
|
||||
int uv___stream_fd(const uv_stream_t* handle);
|
||||
#define uv__stream_fd(handle) (uv___stream_fd((const uv_stream_t*) (handle)))
|
||||
#else
|
||||
#define uv__stream_fd(handle) ((handle)->io_watcher.fd)
|
||||
#endif /* defined(__APPLE__) */
|
||||
|
||||
#ifdef UV__O_NONBLOCK
|
||||
# define UV__F_NONBLOCK UV__O_NONBLOCK
|
||||
#else
|
||||
# define UV__F_NONBLOCK 1
|
||||
#endif
|
||||
|
||||
int uv__make_socketpair(int fds[2], int flags);
|
||||
int uv__make_pipe(int fds[2], int flags);
|
||||
|
||||
#if defined(__APPLE__)
|
||||
|
||||
int uv__fsevents_init(uv_fs_event_t* handle);
|
||||
int uv__fsevents_close(uv_fs_event_t* handle);
|
||||
void uv__fsevents_loop_delete(uv_loop_t* loop);
|
||||
|
||||
/* OSX < 10.7 has no file events, polyfill them */
|
||||
#ifndef MAC_OS_X_VERSION_10_7
|
||||
|
||||
static const int kFSEventStreamCreateFlagFileEvents = 0x00000010;
|
||||
static const int kFSEventStreamEventFlagItemCreated = 0x00000100;
|
||||
static const int kFSEventStreamEventFlagItemRemoved = 0x00000200;
|
||||
static const int kFSEventStreamEventFlagItemInodeMetaMod = 0x00000400;
|
||||
static const int kFSEventStreamEventFlagItemRenamed = 0x00000800;
|
||||
static const int kFSEventStreamEventFlagItemModified = 0x00001000;
|
||||
static const int kFSEventStreamEventFlagItemFinderInfoMod = 0x00002000;
|
||||
static const int kFSEventStreamEventFlagItemChangeOwner = 0x00004000;
|
||||
static const int kFSEventStreamEventFlagItemXattrMod = 0x00008000;
|
||||
static const int kFSEventStreamEventFlagItemIsFile = 0x00010000;
|
||||
static const int kFSEventStreamEventFlagItemIsDir = 0x00020000;
|
||||
static const int kFSEventStreamEventFlagItemIsSymlink = 0x00040000;
|
||||
|
||||
#endif /* __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 1070 */
|
||||
|
||||
#endif /* defined(__APPLE__) */
|
||||
|
||||
UV_UNUSED(static void uv__update_time(uv_loop_t* loop)) {
|
||||
/* Use a fast time source if available. We only need millisecond precision.
|
||||
*/
|
||||
loop->time = uv__hrtime(UV_CLOCK_FAST) / 1000000;
|
||||
}
|
||||
|
||||
UV_UNUSED(static char* uv__basename_r(const char* path)) {
|
||||
char* s;
|
||||
|
||||
s = strrchr(path, '/');
|
||||
if (s == NULL)
|
||||
return (char*) path;
|
||||
|
||||
return s + 1;
|
||||
}
|
||||
|
||||
#if defined(__linux__)
|
||||
int uv__inotify_fork(uv_loop_t* loop, void* old_watchers);
|
||||
#endif
|
||||
|
||||
#endif /* UV_UNIX_INTERNAL_H_ */
|
||||
@@ -0,0 +1,532 @@
|
||||
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "uv.h"
|
||||
#include "internal.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/event.h>
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <time.h>
|
||||
|
||||
/*
|
||||
* Required on
|
||||
* - Until at least FreeBSD 11.0
|
||||
* - Older versions of Mac OS X
|
||||
*
|
||||
* http://www.boost.org/doc/libs/1_61_0/boost/asio/detail/kqueue_reactor.hpp
|
||||
*/
|
||||
#ifndef EV_OOBAND
|
||||
#define EV_OOBAND EV_FLAG1
|
||||
#endif
|
||||
|
||||
static void uv__fs_event(uv_loop_t* loop, uv__io_t* w, unsigned int fflags);
|
||||
|
||||
|
||||
int uv__kqueue_init(uv_loop_t* loop) {
|
||||
loop->backend_fd = kqueue();
|
||||
if (loop->backend_fd == -1)
|
||||
return -errno;
|
||||
|
||||
uv__cloexec(loop->backend_fd, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int uv__has_forked_with_cfrunloop;
|
||||
|
||||
int uv__io_fork(uv_loop_t* loop) {
|
||||
int err;
|
||||
uv__close(loop->backend_fd);
|
||||
loop->backend_fd = -1;
|
||||
err = uv__kqueue_init(loop);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
#if defined(__APPLE__)
|
||||
if (loop->cf_state != NULL) {
|
||||
/* We cannot start another CFRunloop and/or thread in the child
|
||||
process; CF aborts if you try or if you try to touch the thread
|
||||
at all to kill it. So the best we can do is ignore it from now
|
||||
on. This means we can't watch directories in the same way
|
||||
anymore (like other BSDs). It also means we cannot properly
|
||||
clean up the allocated resources; calling
|
||||
uv__fsevents_loop_delete from uv_loop_close will crash the
|
||||
process. So we sidestep the issue by pretending like we never
|
||||
started it in the first place.
|
||||
*/
|
||||
uv__has_forked_with_cfrunloop = 1;
|
||||
uv__free(loop->cf_state);
|
||||
loop->cf_state = NULL;
|
||||
}
|
||||
#endif
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
int uv__io_check_fd(uv_loop_t* loop, int fd) {
|
||||
struct kevent ev;
|
||||
int rc;
|
||||
|
||||
rc = 0;
|
||||
EV_SET(&ev, fd, EVFILT_READ, EV_ADD, 0, 0, 0);
|
||||
if (kevent(loop->backend_fd, &ev, 1, NULL, 0, NULL))
|
||||
rc = -errno;
|
||||
|
||||
EV_SET(&ev, fd, EVFILT_READ, EV_DELETE, 0, 0, 0);
|
||||
if (rc == 0)
|
||||
if (kevent(loop->backend_fd, &ev, 1, NULL, 0, NULL))
|
||||
abort();
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
void uv__io_poll(uv_loop_t* loop, int timeout) {
|
||||
struct kevent events[1024];
|
||||
struct kevent* ev;
|
||||
struct timespec spec;
|
||||
unsigned int nevents;
|
||||
unsigned int revents;
|
||||
QUEUE* q;
|
||||
uv__io_t* w;
|
||||
sigset_t* pset;
|
||||
sigset_t set;
|
||||
uint64_t base;
|
||||
uint64_t diff;
|
||||
int have_signals;
|
||||
int filter;
|
||||
int fflags;
|
||||
int count;
|
||||
int nfds;
|
||||
int fd;
|
||||
int op;
|
||||
int i;
|
||||
|
||||
if (loop->nfds == 0) {
|
||||
assert(QUEUE_EMPTY(&loop->watcher_queue));
|
||||
return;
|
||||
}
|
||||
|
||||
nevents = 0;
|
||||
|
||||
while (!QUEUE_EMPTY(&loop->watcher_queue)) {
|
||||
q = QUEUE_HEAD(&loop->watcher_queue);
|
||||
QUEUE_REMOVE(q);
|
||||
QUEUE_INIT(q);
|
||||
|
||||
w = QUEUE_DATA(q, uv__io_t, watcher_queue);
|
||||
assert(w->pevents != 0);
|
||||
assert(w->fd >= 0);
|
||||
assert(w->fd < (int) loop->nwatchers);
|
||||
|
||||
if ((w->events & POLLIN) == 0 && (w->pevents & POLLIN) != 0) {
|
||||
filter = EVFILT_READ;
|
||||
fflags = 0;
|
||||
op = EV_ADD;
|
||||
|
||||
if (w->cb == uv__fs_event) {
|
||||
filter = EVFILT_VNODE;
|
||||
fflags = NOTE_ATTRIB | NOTE_WRITE | NOTE_RENAME
|
||||
| NOTE_DELETE | NOTE_EXTEND | NOTE_REVOKE;
|
||||
op = EV_ADD | EV_ONESHOT; /* Stop the event from firing repeatedly. */
|
||||
}
|
||||
|
||||
EV_SET(events + nevents, w->fd, filter, op, fflags, 0, 0);
|
||||
|
||||
if (++nevents == ARRAY_SIZE(events)) {
|
||||
if (kevent(loop->backend_fd, events, nevents, NULL, 0, NULL))
|
||||
abort();
|
||||
nevents = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if ((w->events & POLLOUT) == 0 && (w->pevents & POLLOUT) != 0) {
|
||||
EV_SET(events + nevents, w->fd, EVFILT_WRITE, EV_ADD, 0, 0, 0);
|
||||
|
||||
if (++nevents == ARRAY_SIZE(events)) {
|
||||
if (kevent(loop->backend_fd, events, nevents, NULL, 0, NULL))
|
||||
abort();
|
||||
nevents = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if ((w->events & UV__POLLPRI) == 0 && (w->pevents & UV__POLLPRI) != 0) {
|
||||
EV_SET(events + nevents, w->fd, EV_OOBAND, EV_ADD, 0, 0, 0);
|
||||
|
||||
if (++nevents == ARRAY_SIZE(events)) {
|
||||
if (kevent(loop->backend_fd, events, nevents, NULL, 0, NULL))
|
||||
abort();
|
||||
nevents = 0;
|
||||
}
|
||||
}
|
||||
|
||||
w->events = w->pevents;
|
||||
}
|
||||
|
||||
pset = NULL;
|
||||
if (loop->flags & UV_LOOP_BLOCK_SIGPROF) {
|
||||
pset = &set;
|
||||
sigemptyset(pset);
|
||||
sigaddset(pset, SIGPROF);
|
||||
}
|
||||
|
||||
assert(timeout >= -1);
|
||||
base = loop->time;
|
||||
count = 48; /* Benchmarks suggest this gives the best throughput. */
|
||||
|
||||
for (;; nevents = 0) {
|
||||
if (timeout != -1) {
|
||||
spec.tv_sec = timeout / 1000;
|
||||
spec.tv_nsec = (timeout % 1000) * 1000000;
|
||||
}
|
||||
|
||||
if (pset != NULL)
|
||||
pthread_sigmask(SIG_BLOCK, pset, NULL);
|
||||
|
||||
nfds = kevent(loop->backend_fd,
|
||||
events,
|
||||
nevents,
|
||||
events,
|
||||
ARRAY_SIZE(events),
|
||||
timeout == -1 ? NULL : &spec);
|
||||
|
||||
if (pset != NULL)
|
||||
pthread_sigmask(SIG_UNBLOCK, pset, NULL);
|
||||
|
||||
/* Update loop->time unconditionally. It's tempting to skip the update when
|
||||
* timeout == 0 (i.e. non-blocking poll) but there is no guarantee that the
|
||||
* operating system didn't reschedule our process while in the syscall.
|
||||
*/
|
||||
SAVE_ERRNO(uv__update_time(loop));
|
||||
|
||||
if (nfds == 0) {
|
||||
assert(timeout != -1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (nfds == -1) {
|
||||
if (errno != EINTR)
|
||||
abort();
|
||||
|
||||
if (timeout == 0)
|
||||
return;
|
||||
|
||||
if (timeout == -1)
|
||||
continue;
|
||||
|
||||
/* Interrupted by a signal. Update timeout and poll again. */
|
||||
goto update_timeout;
|
||||
}
|
||||
|
||||
have_signals = 0;
|
||||
nevents = 0;
|
||||
|
||||
assert(loop->watchers != NULL);
|
||||
loop->watchers[loop->nwatchers] = (void*) events;
|
||||
loop->watchers[loop->nwatchers + 1] = (void*) (uintptr_t) nfds;
|
||||
for (i = 0; i < nfds; i++) {
|
||||
ev = events + i;
|
||||
fd = ev->ident;
|
||||
/* Skip invalidated events, see uv__platform_invalidate_fd */
|
||||
if (fd == -1)
|
||||
continue;
|
||||
w = loop->watchers[fd];
|
||||
|
||||
if (w == NULL) {
|
||||
/* File descriptor that we've stopped watching, disarm it. */
|
||||
/* TODO batch up */
|
||||
struct kevent events[1];
|
||||
|
||||
EV_SET(events + 0, fd, ev->filter, EV_DELETE, 0, 0, 0);
|
||||
if (kevent(loop->backend_fd, events, 1, NULL, 0, NULL))
|
||||
if (errno != EBADF && errno != ENOENT)
|
||||
abort();
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ev->filter == EVFILT_VNODE) {
|
||||
assert(w->events == POLLIN);
|
||||
assert(w->pevents == POLLIN);
|
||||
w->cb(loop, w, ev->fflags); /* XXX always uv__fs_event() */
|
||||
nevents++;
|
||||
continue;
|
||||
}
|
||||
|
||||
revents = 0;
|
||||
|
||||
if (ev->filter == EVFILT_READ) {
|
||||
if (w->pevents & POLLIN) {
|
||||
revents |= POLLIN;
|
||||
w->rcount = ev->data;
|
||||
} else {
|
||||
/* TODO batch up */
|
||||
struct kevent events[1];
|
||||
EV_SET(events + 0, fd, ev->filter, EV_DELETE, 0, 0, 0);
|
||||
if (kevent(loop->backend_fd, events, 1, NULL, 0, NULL))
|
||||
if (errno != ENOENT)
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
if (ev->filter == EV_OOBAND) {
|
||||
if (w->pevents & UV__POLLPRI) {
|
||||
revents |= UV__POLLPRI;
|
||||
w->rcount = ev->data;
|
||||
} else {
|
||||
/* TODO batch up */
|
||||
struct kevent events[1];
|
||||
EV_SET(events + 0, fd, ev->filter, EV_DELETE, 0, 0, 0);
|
||||
if (kevent(loop->backend_fd, events, 1, NULL, 0, NULL))
|
||||
if (errno != ENOENT)
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
if (ev->filter == EVFILT_WRITE) {
|
||||
if (w->pevents & POLLOUT) {
|
||||
revents |= POLLOUT;
|
||||
w->wcount = ev->data;
|
||||
} else {
|
||||
/* TODO batch up */
|
||||
struct kevent events[1];
|
||||
EV_SET(events + 0, fd, ev->filter, EV_DELETE, 0, 0, 0);
|
||||
if (kevent(loop->backend_fd, events, 1, NULL, 0, NULL))
|
||||
if (errno != ENOENT)
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
if (ev->flags & EV_ERROR)
|
||||
revents |= POLLERR;
|
||||
|
||||
if ((ev->flags & EV_EOF) && (w->pevents & UV__POLLRDHUP))
|
||||
revents |= UV__POLLRDHUP;
|
||||
|
||||
if (revents == 0)
|
||||
continue;
|
||||
|
||||
/* Run signal watchers last. This also affects child process watchers
|
||||
* because those are implemented in terms of signal watchers.
|
||||
*/
|
||||
if (w == &loop->signal_io_watcher)
|
||||
have_signals = 1;
|
||||
else
|
||||
w->cb(loop, w, revents);
|
||||
|
||||
nevents++;
|
||||
}
|
||||
|
||||
if (have_signals != 0)
|
||||
loop->signal_io_watcher.cb(loop, &loop->signal_io_watcher, POLLIN);
|
||||
|
||||
loop->watchers[loop->nwatchers] = NULL;
|
||||
loop->watchers[loop->nwatchers + 1] = NULL;
|
||||
|
||||
if (have_signals != 0)
|
||||
return; /* Event loop should cycle now so don't poll again. */
|
||||
|
||||
if (nevents != 0) {
|
||||
if (nfds == ARRAY_SIZE(events) && --count != 0) {
|
||||
/* Poll for more events but don't block this time. */
|
||||
timeout = 0;
|
||||
continue;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (timeout == 0)
|
||||
return;
|
||||
|
||||
if (timeout == -1)
|
||||
continue;
|
||||
|
||||
update_timeout:
|
||||
assert(timeout > 0);
|
||||
|
||||
diff = loop->time - base;
|
||||
if (diff >= (uint64_t) timeout)
|
||||
return;
|
||||
|
||||
timeout -= diff;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) {
|
||||
struct kevent* events;
|
||||
uintptr_t i;
|
||||
uintptr_t nfds;
|
||||
|
||||
assert(loop->watchers != NULL);
|
||||
|
||||
events = (struct kevent*) loop->watchers[loop->nwatchers];
|
||||
nfds = (uintptr_t) loop->watchers[loop->nwatchers + 1];
|
||||
if (events == NULL)
|
||||
return;
|
||||
|
||||
/* Invalidate events with same file descriptor */
|
||||
for (i = 0; i < nfds; i++)
|
||||
if ((int) events[i].ident == fd)
|
||||
events[i].ident = -1;
|
||||
}
|
||||
|
||||
|
||||
static void uv__fs_event(uv_loop_t* loop, uv__io_t* w, unsigned int fflags) {
|
||||
uv_fs_event_t* handle;
|
||||
struct kevent ev;
|
||||
int events;
|
||||
const char* path;
|
||||
#if defined(F_GETPATH)
|
||||
/* MAXPATHLEN == PATH_MAX but the former is what XNU calls it internally. */
|
||||
char pathbuf[MAXPATHLEN];
|
||||
#endif
|
||||
|
||||
handle = container_of(w, uv_fs_event_t, event_watcher);
|
||||
|
||||
if (fflags & (NOTE_ATTRIB | NOTE_EXTEND))
|
||||
events = UV_CHANGE;
|
||||
else
|
||||
events = UV_RENAME;
|
||||
|
||||
path = NULL;
|
||||
#if defined(F_GETPATH)
|
||||
/* Also works when the file has been unlinked from the file system. Passing
|
||||
* in the path when the file has been deleted is arguably a little strange
|
||||
* but it's consistent with what the inotify backend does.
|
||||
*/
|
||||
if (fcntl(handle->event_watcher.fd, F_GETPATH, pathbuf) == 0)
|
||||
path = uv__basename_r(pathbuf);
|
||||
#endif
|
||||
handle->cb(handle, path, events, 0);
|
||||
|
||||
if (handle->event_watcher.fd == -1)
|
||||
return;
|
||||
|
||||
/* Watcher operates in one-shot mode, re-arm it. */
|
||||
fflags = NOTE_ATTRIB | NOTE_WRITE | NOTE_RENAME
|
||||
| NOTE_DELETE | NOTE_EXTEND | NOTE_REVOKE;
|
||||
|
||||
EV_SET(&ev, w->fd, EVFILT_VNODE, EV_ADD | EV_ONESHOT, fflags, 0, 0);
|
||||
|
||||
if (kevent(loop->backend_fd, &ev, 1, NULL, 0, NULL))
|
||||
abort();
|
||||
}
|
||||
|
||||
|
||||
int uv_fs_event_init(uv_loop_t* loop, uv_fs_event_t* handle) {
|
||||
uv__handle_init(loop, (uv_handle_t*)handle, UV_FS_EVENT);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv_fs_event_start(uv_fs_event_t* handle,
|
||||
uv_fs_event_cb cb,
|
||||
const char* path,
|
||||
unsigned int flags) {
|
||||
#if defined(__APPLE__)
|
||||
struct stat statbuf;
|
||||
#endif /* defined(__APPLE__) */
|
||||
int fd;
|
||||
|
||||
if (uv__is_active(handle))
|
||||
return -EINVAL;
|
||||
|
||||
/* TODO open asynchronously - but how do we report back errors? */
|
||||
fd = open(path, O_RDONLY);
|
||||
if (fd == -1)
|
||||
return -errno;
|
||||
|
||||
uv__handle_start(handle);
|
||||
uv__io_init(&handle->event_watcher, uv__fs_event, fd);
|
||||
handle->path = uv__strdup(path);
|
||||
handle->cb = cb;
|
||||
|
||||
#if defined(__APPLE__)
|
||||
if (uv__has_forked_with_cfrunloop)
|
||||
goto fallback;
|
||||
|
||||
/* Nullify field to perform checks later */
|
||||
handle->cf_cb = NULL;
|
||||
handle->realpath = NULL;
|
||||
handle->realpath_len = 0;
|
||||
handle->cf_flags = flags;
|
||||
|
||||
if (fstat(fd, &statbuf))
|
||||
goto fallback;
|
||||
/* FSEvents works only with directories */
|
||||
if (!(statbuf.st_mode & S_IFDIR))
|
||||
goto fallback;
|
||||
|
||||
/* The fallback fd is no longer needed */
|
||||
uv__close(fd);
|
||||
handle->event_watcher.fd = -1;
|
||||
|
||||
return uv__fsevents_init(handle);
|
||||
|
||||
fallback:
|
||||
#endif /* defined(__APPLE__) */
|
||||
|
||||
uv__io_start(handle->loop, &handle->event_watcher, POLLIN);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv_fs_event_stop(uv_fs_event_t* handle) {
|
||||
if (!uv__is_active(handle))
|
||||
return 0;
|
||||
|
||||
uv__handle_stop(handle);
|
||||
|
||||
#if defined(__APPLE__)
|
||||
if (uv__has_forked_with_cfrunloop || uv__fsevents_close(handle))
|
||||
#endif /* defined(__APPLE__) */
|
||||
{
|
||||
uv__io_close(handle->loop, &handle->event_watcher);
|
||||
}
|
||||
|
||||
uv__free(handle->path);
|
||||
handle->path = NULL;
|
||||
|
||||
if (handle->event_watcher.fd != -1) {
|
||||
/* When FSEvents is used, we don't use the event_watcher's fd under certain
|
||||
* confitions. (see uv_fs_event_start) */
|
||||
uv__close(handle->event_watcher.fd);
|
||||
handle->event_watcher.fd = -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void uv__fs_event_close(uv_fs_event_t* handle) {
|
||||
uv_fs_event_stop(handle);
|
||||
}
|
||||
@@ -0,0 +1,951 @@
|
||||
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/* We lean on the fact that POLL{IN,OUT,ERR,HUP} correspond with their
|
||||
* EPOLL* counterparts. We use the POLL* variants in this file because that
|
||||
* is what libuv uses elsewhere and it avoids a dependency on <sys/epoll.h>.
|
||||
*/
|
||||
|
||||
#include "uv.h"
|
||||
#include "internal.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <net/if.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/sysinfo.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <time.h>
|
||||
|
||||
#define HAVE_IFADDRS_H 1
|
||||
|
||||
#ifdef __UCLIBC__
|
||||
# if __UCLIBC_MAJOR__ < 0 && __UCLIBC_MINOR__ < 9 && __UCLIBC_SUBLEVEL__ < 32
|
||||
# undef HAVE_IFADDRS_H
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_IFADDRS_H
|
||||
# if defined(__ANDROID__)
|
||||
# include "android-ifaddrs.h"
|
||||
# else
|
||||
# include <ifaddrs.h>
|
||||
# endif
|
||||
# include <sys/socket.h>
|
||||
# include <net/ethernet.h>
|
||||
# include <netpacket/packet.h>
|
||||
#endif /* HAVE_IFADDRS_H */
|
||||
|
||||
/* Available from 2.6.32 onwards. */
|
||||
#ifndef CLOCK_MONOTONIC_COARSE
|
||||
# define CLOCK_MONOTONIC_COARSE 6
|
||||
#endif
|
||||
|
||||
/* This is rather annoying: CLOCK_BOOTTIME lives in <linux/time.h> but we can't
|
||||
* include that file because it conflicts with <time.h>. We'll just have to
|
||||
* define it ourselves.
|
||||
*/
|
||||
#ifndef CLOCK_BOOTTIME
|
||||
# define CLOCK_BOOTTIME 7
|
||||
#endif
|
||||
|
||||
static int read_models(unsigned int numcpus, uv_cpu_info_t* ci);
|
||||
static int read_times(FILE* statfile_fp,
|
||||
unsigned int numcpus,
|
||||
uv_cpu_info_t* ci);
|
||||
static void read_speeds(unsigned int numcpus, uv_cpu_info_t* ci);
|
||||
static unsigned long read_cpufreq(unsigned int cpunum);
|
||||
|
||||
|
||||
int uv__platform_loop_init(uv_loop_t* loop) {
|
||||
int fd;
|
||||
|
||||
fd = uv__epoll_create1(UV__EPOLL_CLOEXEC);
|
||||
|
||||
/* epoll_create1() can fail either because it's not implemented (old kernel)
|
||||
* or because it doesn't understand the EPOLL_CLOEXEC flag.
|
||||
*/
|
||||
if (fd == -1 && (errno == ENOSYS || errno == EINVAL)) {
|
||||
fd = uv__epoll_create(256);
|
||||
|
||||
if (fd != -1)
|
||||
uv__cloexec(fd, 1);
|
||||
}
|
||||
|
||||
loop->backend_fd = fd;
|
||||
loop->inotify_fd = -1;
|
||||
loop->inotify_watchers = NULL;
|
||||
|
||||
if (fd == -1)
|
||||
return -errno;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv__io_fork(uv_loop_t* loop) {
|
||||
int err;
|
||||
void* old_watchers;
|
||||
|
||||
old_watchers = loop->inotify_watchers;
|
||||
|
||||
uv__close(loop->backend_fd);
|
||||
loop->backend_fd = -1;
|
||||
uv__platform_loop_delete(loop);
|
||||
|
||||
err = uv__platform_loop_init(loop);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return uv__inotify_fork(loop, old_watchers);
|
||||
}
|
||||
|
||||
|
||||
void uv__platform_loop_delete(uv_loop_t* loop) {
|
||||
if (loop->inotify_fd == -1) return;
|
||||
uv__io_stop(loop, &loop->inotify_read_watcher, POLLIN);
|
||||
uv__close(loop->inotify_fd);
|
||||
loop->inotify_fd = -1;
|
||||
}
|
||||
|
||||
|
||||
void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) {
|
||||
struct uv__epoll_event* events;
|
||||
struct uv__epoll_event dummy;
|
||||
uintptr_t i;
|
||||
uintptr_t nfds;
|
||||
|
||||
assert(loop->watchers != NULL);
|
||||
|
||||
events = (struct uv__epoll_event*) loop->watchers[loop->nwatchers];
|
||||
nfds = (uintptr_t) loop->watchers[loop->nwatchers + 1];
|
||||
if (events != NULL)
|
||||
/* Invalidate events with same file descriptor */
|
||||
for (i = 0; i < nfds; i++)
|
||||
if ((int) events[i].data == fd)
|
||||
events[i].data = -1;
|
||||
|
||||
/* Remove the file descriptor from the epoll.
|
||||
* This avoids a problem where the same file description remains open
|
||||
* in another process, causing repeated junk epoll events.
|
||||
*
|
||||
* We pass in a dummy epoll_event, to work around a bug in old kernels.
|
||||
*/
|
||||
if (loop->backend_fd >= 0) {
|
||||
/* Work around a bug in kernels 3.10 to 3.19 where passing a struct that
|
||||
* has the EPOLLWAKEUP flag set generates spurious audit syslog warnings.
|
||||
*/
|
||||
memset(&dummy, 0, sizeof(dummy));
|
||||
uv__epoll_ctl(loop->backend_fd, UV__EPOLL_CTL_DEL, fd, &dummy);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int uv__io_check_fd(uv_loop_t* loop, int fd) {
|
||||
struct uv__epoll_event e;
|
||||
int rc;
|
||||
|
||||
e.events = POLLIN;
|
||||
e.data = -1;
|
||||
|
||||
rc = 0;
|
||||
if (uv__epoll_ctl(loop->backend_fd, UV__EPOLL_CTL_ADD, fd, &e))
|
||||
if (errno != EEXIST)
|
||||
rc = -errno;
|
||||
|
||||
if (rc == 0)
|
||||
if (uv__epoll_ctl(loop->backend_fd, UV__EPOLL_CTL_DEL, fd, &e))
|
||||
abort();
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
void uv__io_poll(uv_loop_t* loop, int timeout) {
|
||||
/* A bug in kernels < 2.6.37 makes timeouts larger than ~30 minutes
|
||||
* effectively infinite on 32 bits architectures. To avoid blocking
|
||||
* indefinitely, we cap the timeout and poll again if necessary.
|
||||
*
|
||||
* Note that "30 minutes" is a simplification because it depends on
|
||||
* the value of CONFIG_HZ. The magic constant assumes CONFIG_HZ=1200,
|
||||
* that being the largest value I have seen in the wild (and only once.)
|
||||
*/
|
||||
static const int max_safe_timeout = 1789569;
|
||||
static int no_epoll_pwait;
|
||||
static int no_epoll_wait;
|
||||
struct uv__epoll_event events[1024];
|
||||
struct uv__epoll_event* pe;
|
||||
struct uv__epoll_event e;
|
||||
int real_timeout;
|
||||
QUEUE* q;
|
||||
uv__io_t* w;
|
||||
sigset_t sigset;
|
||||
uint64_t sigmask;
|
||||
uint64_t base;
|
||||
int have_signals;
|
||||
int nevents;
|
||||
int count;
|
||||
int nfds;
|
||||
int fd;
|
||||
int op;
|
||||
int i;
|
||||
|
||||
if (loop->nfds == 0) {
|
||||
assert(QUEUE_EMPTY(&loop->watcher_queue));
|
||||
return;
|
||||
}
|
||||
|
||||
while (!QUEUE_EMPTY(&loop->watcher_queue)) {
|
||||
q = QUEUE_HEAD(&loop->watcher_queue);
|
||||
QUEUE_REMOVE(q);
|
||||
QUEUE_INIT(q);
|
||||
|
||||
w = QUEUE_DATA(q, uv__io_t, watcher_queue);
|
||||
assert(w->pevents != 0);
|
||||
assert(w->fd >= 0);
|
||||
assert(w->fd < (int) loop->nwatchers);
|
||||
|
||||
e.events = w->pevents;
|
||||
e.data = w->fd;
|
||||
|
||||
if (w->events == 0)
|
||||
op = UV__EPOLL_CTL_ADD;
|
||||
else
|
||||
op = UV__EPOLL_CTL_MOD;
|
||||
|
||||
/* XXX Future optimization: do EPOLL_CTL_MOD lazily if we stop watching
|
||||
* events, skip the syscall and squelch the events after epoll_wait().
|
||||
*/
|
||||
if (uv__epoll_ctl(loop->backend_fd, op, w->fd, &e)) {
|
||||
if (errno != EEXIST)
|
||||
abort();
|
||||
|
||||
assert(op == UV__EPOLL_CTL_ADD);
|
||||
|
||||
/* We've reactivated a file descriptor that's been watched before. */
|
||||
if (uv__epoll_ctl(loop->backend_fd, UV__EPOLL_CTL_MOD, w->fd, &e))
|
||||
abort();
|
||||
}
|
||||
|
||||
w->events = w->pevents;
|
||||
}
|
||||
|
||||
sigmask = 0;
|
||||
if (loop->flags & UV_LOOP_BLOCK_SIGPROF) {
|
||||
sigemptyset(&sigset);
|
||||
sigaddset(&sigset, SIGPROF);
|
||||
sigmask |= 1 << (SIGPROF - 1);
|
||||
}
|
||||
|
||||
assert(timeout >= -1);
|
||||
base = loop->time;
|
||||
count = 48; /* Benchmarks suggest this gives the best throughput. */
|
||||
real_timeout = timeout;
|
||||
|
||||
for (;;) {
|
||||
/* See the comment for max_safe_timeout for an explanation of why
|
||||
* this is necessary. Executive summary: kernel bug workaround.
|
||||
*/
|
||||
if (sizeof(int32_t) == sizeof(long) && timeout >= max_safe_timeout)
|
||||
timeout = max_safe_timeout;
|
||||
|
||||
if (sigmask != 0 && no_epoll_pwait != 0)
|
||||
if (pthread_sigmask(SIG_BLOCK, &sigset, NULL))
|
||||
abort();
|
||||
|
||||
if (no_epoll_wait != 0 || (sigmask != 0 && no_epoll_pwait == 0)) {
|
||||
nfds = uv__epoll_pwait(loop->backend_fd,
|
||||
events,
|
||||
ARRAY_SIZE(events),
|
||||
timeout,
|
||||
sigmask);
|
||||
if (nfds == -1 && errno == ENOSYS)
|
||||
no_epoll_pwait = 1;
|
||||
} else {
|
||||
nfds = uv__epoll_wait(loop->backend_fd,
|
||||
events,
|
||||
ARRAY_SIZE(events),
|
||||
timeout);
|
||||
if (nfds == -1 && errno == ENOSYS)
|
||||
no_epoll_wait = 1;
|
||||
}
|
||||
|
||||
if (sigmask != 0 && no_epoll_pwait != 0)
|
||||
if (pthread_sigmask(SIG_UNBLOCK, &sigset, NULL))
|
||||
abort();
|
||||
|
||||
/* Update loop->time unconditionally. It's tempting to skip the update when
|
||||
* timeout == 0 (i.e. non-blocking poll) but there is no guarantee that the
|
||||
* operating system didn't reschedule our process while in the syscall.
|
||||
*/
|
||||
SAVE_ERRNO(uv__update_time(loop));
|
||||
|
||||
if (nfds == 0) {
|
||||
assert(timeout != -1);
|
||||
|
||||
if (timeout == 0)
|
||||
return;
|
||||
|
||||
/* We may have been inside the system call for longer than |timeout|
|
||||
* milliseconds so we need to update the timestamp to avoid drift.
|
||||
*/
|
||||
goto update_timeout;
|
||||
}
|
||||
|
||||
if (nfds == -1) {
|
||||
if (errno == ENOSYS) {
|
||||
/* epoll_wait() or epoll_pwait() failed, try the other system call. */
|
||||
assert(no_epoll_wait == 0 || no_epoll_pwait == 0);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (errno != EINTR)
|
||||
abort();
|
||||
|
||||
if (timeout == -1)
|
||||
continue;
|
||||
|
||||
if (timeout == 0)
|
||||
return;
|
||||
|
||||
/* Interrupted by a signal. Update timeout and poll again. */
|
||||
goto update_timeout;
|
||||
}
|
||||
|
||||
have_signals = 0;
|
||||
nevents = 0;
|
||||
|
||||
assert(loop->watchers != NULL);
|
||||
loop->watchers[loop->nwatchers] = (void*) events;
|
||||
loop->watchers[loop->nwatchers + 1] = (void*) (uintptr_t) nfds;
|
||||
for (i = 0; i < nfds; i++) {
|
||||
pe = events + i;
|
||||
fd = pe->data;
|
||||
|
||||
/* Skip invalidated events, see uv__platform_invalidate_fd */
|
||||
if (fd == -1)
|
||||
continue;
|
||||
|
||||
assert(fd >= 0);
|
||||
assert((unsigned) fd < loop->nwatchers);
|
||||
|
||||
w = loop->watchers[fd];
|
||||
|
||||
if (w == NULL) {
|
||||
/* File descriptor that we've stopped watching, disarm it.
|
||||
*
|
||||
* Ignore all errors because we may be racing with another thread
|
||||
* when the file descriptor is closed.
|
||||
*/
|
||||
uv__epoll_ctl(loop->backend_fd, UV__EPOLL_CTL_DEL, fd, pe);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Give users only events they're interested in. Prevents spurious
|
||||
* callbacks when previous callback invocation in this loop has stopped
|
||||
* the current watcher. Also, filters out events that users has not
|
||||
* requested us to watch.
|
||||
*/
|
||||
pe->events &= w->pevents | POLLERR | POLLHUP;
|
||||
|
||||
/* Work around an epoll quirk where it sometimes reports just the
|
||||
* EPOLLERR or EPOLLHUP event. In order to force the event loop to
|
||||
* move forward, we merge in the read/write events that the watcher
|
||||
* is interested in; uv__read() and uv__write() will then deal with
|
||||
* the error or hangup in the usual fashion.
|
||||
*
|
||||
* Note to self: happens when epoll reports EPOLLIN|EPOLLHUP, the user
|
||||
* reads the available data, calls uv_read_stop(), then sometime later
|
||||
* calls uv_read_start() again. By then, libuv has forgotten about the
|
||||
* hangup and the kernel won't report EPOLLIN again because there's
|
||||
* nothing left to read. If anything, libuv is to blame here. The
|
||||
* current hack is just a quick bandaid; to properly fix it, libuv
|
||||
* needs to remember the error/hangup event. We should get that for
|
||||
* free when we switch over to edge-triggered I/O.
|
||||
*/
|
||||
if (pe->events == POLLERR || pe->events == POLLHUP)
|
||||
pe->events |= w->pevents & (POLLIN | POLLOUT | UV__POLLPRI);
|
||||
|
||||
if (pe->events != 0) {
|
||||
/* Run signal watchers last. This also affects child process watchers
|
||||
* because those are implemented in terms of signal watchers.
|
||||
*/
|
||||
if (w == &loop->signal_io_watcher)
|
||||
have_signals = 1;
|
||||
else
|
||||
w->cb(loop, w, pe->events);
|
||||
|
||||
nevents++;
|
||||
}
|
||||
}
|
||||
|
||||
if (have_signals != 0)
|
||||
loop->signal_io_watcher.cb(loop, &loop->signal_io_watcher, POLLIN);
|
||||
|
||||
loop->watchers[loop->nwatchers] = NULL;
|
||||
loop->watchers[loop->nwatchers + 1] = NULL;
|
||||
|
||||
if (have_signals != 0)
|
||||
return; /* Event loop should cycle now so don't poll again. */
|
||||
|
||||
if (nevents != 0) {
|
||||
if (nfds == ARRAY_SIZE(events) && --count != 0) {
|
||||
/* Poll for more events but don't block this time. */
|
||||
timeout = 0;
|
||||
continue;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (timeout == 0)
|
||||
return;
|
||||
|
||||
if (timeout == -1)
|
||||
continue;
|
||||
|
||||
update_timeout:
|
||||
assert(timeout > 0);
|
||||
|
||||
real_timeout -= (loop->time - base);
|
||||
if (real_timeout <= 0)
|
||||
return;
|
||||
|
||||
timeout = real_timeout;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
uint64_t uv__hrtime(uv_clocktype_t type) {
|
||||
static clock_t fast_clock_id = -1;
|
||||
struct timespec t;
|
||||
clock_t clock_id;
|
||||
|
||||
/* Prefer CLOCK_MONOTONIC_COARSE if available but only when it has
|
||||
* millisecond granularity or better. CLOCK_MONOTONIC_COARSE is
|
||||
* serviced entirely from the vDSO, whereas CLOCK_MONOTONIC may
|
||||
* decide to make a costly system call.
|
||||
*/
|
||||
/* TODO(bnoordhuis) Use CLOCK_MONOTONIC_COARSE for UV_CLOCK_PRECISE
|
||||
* when it has microsecond granularity or better (unlikely).
|
||||
*/
|
||||
if (type == UV_CLOCK_FAST && fast_clock_id == -1) {
|
||||
if (clock_getres(CLOCK_MONOTONIC_COARSE, &t) == 0 &&
|
||||
t.tv_nsec <= 1 * 1000 * 1000) {
|
||||
fast_clock_id = CLOCK_MONOTONIC_COARSE;
|
||||
} else {
|
||||
fast_clock_id = CLOCK_MONOTONIC;
|
||||
}
|
||||
}
|
||||
|
||||
clock_id = CLOCK_MONOTONIC;
|
||||
if (type == UV_CLOCK_FAST)
|
||||
clock_id = fast_clock_id;
|
||||
|
||||
if (clock_gettime(clock_id, &t))
|
||||
return 0; /* Not really possible. */
|
||||
|
||||
return t.tv_sec * (uint64_t) 1e9 + t.tv_nsec;
|
||||
}
|
||||
|
||||
|
||||
int uv_resident_set_memory(size_t* rss) {
|
||||
char buf[1024];
|
||||
const char* s;
|
||||
ssize_t n;
|
||||
long val;
|
||||
int fd;
|
||||
int i;
|
||||
|
||||
do
|
||||
fd = open("/proc/self/stat", O_RDONLY);
|
||||
while (fd == -1 && errno == EINTR);
|
||||
|
||||
if (fd == -1)
|
||||
return -errno;
|
||||
|
||||
do
|
||||
n = read(fd, buf, sizeof(buf) - 1);
|
||||
while (n == -1 && errno == EINTR);
|
||||
|
||||
uv__close(fd);
|
||||
if (n == -1)
|
||||
return -errno;
|
||||
buf[n] = '\0';
|
||||
|
||||
s = strchr(buf, ' ');
|
||||
if (s == NULL)
|
||||
goto err;
|
||||
|
||||
s += 1;
|
||||
if (*s != '(')
|
||||
goto err;
|
||||
|
||||
s = strchr(s, ')');
|
||||
if (s == NULL)
|
||||
goto err;
|
||||
|
||||
for (i = 1; i <= 22; i++) {
|
||||
s = strchr(s + 1, ' ');
|
||||
if (s == NULL)
|
||||
goto err;
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
val = strtol(s, NULL, 10);
|
||||
if (errno != 0)
|
||||
goto err;
|
||||
if (val < 0)
|
||||
goto err;
|
||||
|
||||
*rss = val * getpagesize();
|
||||
return 0;
|
||||
|
||||
err:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
||||
int uv_uptime(double* uptime) {
|
||||
static volatile int no_clock_boottime;
|
||||
struct timespec now;
|
||||
int r;
|
||||
|
||||
/* Try CLOCK_BOOTTIME first, fall back to CLOCK_MONOTONIC if not available
|
||||
* (pre-2.6.39 kernels). CLOCK_MONOTONIC doesn't increase when the system
|
||||
* is suspended.
|
||||
*/
|
||||
if (no_clock_boottime) {
|
||||
retry: r = clock_gettime(CLOCK_MONOTONIC, &now);
|
||||
}
|
||||
else if ((r = clock_gettime(CLOCK_BOOTTIME, &now)) && errno == EINVAL) {
|
||||
no_clock_boottime = 1;
|
||||
goto retry;
|
||||
}
|
||||
|
||||
if (r)
|
||||
return -errno;
|
||||
|
||||
*uptime = now.tv_sec;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int uv__cpu_num(FILE* statfile_fp, unsigned int* numcpus) {
|
||||
unsigned int num;
|
||||
char buf[1024];
|
||||
|
||||
if (!fgets(buf, sizeof(buf), statfile_fp))
|
||||
return -EIO;
|
||||
|
||||
num = 0;
|
||||
while (fgets(buf, sizeof(buf), statfile_fp)) {
|
||||
if (strncmp(buf, "cpu", 3))
|
||||
break;
|
||||
num++;
|
||||
}
|
||||
|
||||
if (num == 0)
|
||||
return -EIO;
|
||||
|
||||
*numcpus = num;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) {
|
||||
unsigned int numcpus;
|
||||
uv_cpu_info_t* ci;
|
||||
int err;
|
||||
FILE* statfile_fp;
|
||||
|
||||
*cpu_infos = NULL;
|
||||
*count = 0;
|
||||
|
||||
statfile_fp = uv__open_file("/proc/stat");
|
||||
if (statfile_fp == NULL)
|
||||
return -errno;
|
||||
|
||||
err = uv__cpu_num(statfile_fp, &numcpus);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
err = -ENOMEM;
|
||||
ci = uv__calloc(numcpus, sizeof(*ci));
|
||||
if (ci == NULL)
|
||||
goto out;
|
||||
|
||||
err = read_models(numcpus, ci);
|
||||
if (err == 0)
|
||||
err = read_times(statfile_fp, numcpus, ci);
|
||||
|
||||
if (err) {
|
||||
uv_free_cpu_info(ci, numcpus);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* read_models() on x86 also reads the CPU speed from /proc/cpuinfo.
|
||||
* We don't check for errors here. Worst case, the field is left zero.
|
||||
*/
|
||||
if (ci[0].speed == 0)
|
||||
read_speeds(numcpus, ci);
|
||||
|
||||
*cpu_infos = ci;
|
||||
*count = numcpus;
|
||||
err = 0;
|
||||
|
||||
out:
|
||||
|
||||
if (fclose(statfile_fp))
|
||||
if (errno != EINTR && errno != EINPROGRESS)
|
||||
abort();
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
static void read_speeds(unsigned int numcpus, uv_cpu_info_t* ci) {
|
||||
unsigned int num;
|
||||
|
||||
for (num = 0; num < numcpus; num++)
|
||||
ci[num].speed = read_cpufreq(num) / 1000;
|
||||
}
|
||||
|
||||
|
||||
/* Also reads the CPU frequency on x86. The other architectures only have
|
||||
* a BogoMIPS field, which may not be very accurate.
|
||||
*
|
||||
* Note: Simply returns on error, uv_cpu_info() takes care of the cleanup.
|
||||
*/
|
||||
static int read_models(unsigned int numcpus, uv_cpu_info_t* ci) {
|
||||
static const char model_marker[] = "model name\t: ";
|
||||
static const char speed_marker[] = "cpu MHz\t\t: ";
|
||||
const char* inferred_model;
|
||||
unsigned int model_idx;
|
||||
unsigned int speed_idx;
|
||||
char buf[1024];
|
||||
char* model;
|
||||
FILE* fp;
|
||||
|
||||
/* Most are unused on non-ARM, non-MIPS and non-x86 architectures. */
|
||||
(void) &model_marker;
|
||||
(void) &speed_marker;
|
||||
(void) &speed_idx;
|
||||
(void) &model;
|
||||
(void) &buf;
|
||||
(void) &fp;
|
||||
|
||||
model_idx = 0;
|
||||
speed_idx = 0;
|
||||
|
||||
#if defined(__arm__) || \
|
||||
defined(__i386__) || \
|
||||
defined(__mips__) || \
|
||||
defined(__x86_64__)
|
||||
fp = uv__open_file("/proc/cpuinfo");
|
||||
if (fp == NULL)
|
||||
return -errno;
|
||||
|
||||
while (fgets(buf, sizeof(buf), fp)) {
|
||||
if (model_idx < numcpus) {
|
||||
if (strncmp(buf, model_marker, sizeof(model_marker) - 1) == 0) {
|
||||
model = buf + sizeof(model_marker) - 1;
|
||||
model = uv__strndup(model, strlen(model) - 1); /* Strip newline. */
|
||||
if (model == NULL) {
|
||||
fclose(fp);
|
||||
return -ENOMEM;
|
||||
}
|
||||
ci[model_idx++].model = model;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
#if defined(__arm__) || defined(__mips__)
|
||||
if (model_idx < numcpus) {
|
||||
#if defined(__arm__)
|
||||
/* Fallback for pre-3.8 kernels. */
|
||||
static const char model_marker[] = "Processor\t: ";
|
||||
#else /* defined(__mips__) */
|
||||
static const char model_marker[] = "cpu model\t\t: ";
|
||||
#endif
|
||||
if (strncmp(buf, model_marker, sizeof(model_marker) - 1) == 0) {
|
||||
model = buf + sizeof(model_marker) - 1;
|
||||
model = uv__strndup(model, strlen(model) - 1); /* Strip newline. */
|
||||
if (model == NULL) {
|
||||
fclose(fp);
|
||||
return -ENOMEM;
|
||||
}
|
||||
ci[model_idx++].model = model;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
#else /* !__arm__ && !__mips__ */
|
||||
if (speed_idx < numcpus) {
|
||||
if (strncmp(buf, speed_marker, sizeof(speed_marker) - 1) == 0) {
|
||||
ci[speed_idx++].speed = atoi(buf + sizeof(speed_marker) - 1);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
#endif /* __arm__ || __mips__ */
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
#endif /* __arm__ || __i386__ || __mips__ || __x86_64__ */
|
||||
|
||||
/* Now we want to make sure that all the models contain *something* because
|
||||
* it's not safe to leave them as null. Copy the last entry unless there
|
||||
* isn't one, in that case we simply put "unknown" into everything.
|
||||
*/
|
||||
inferred_model = "unknown";
|
||||
if (model_idx > 0)
|
||||
inferred_model = ci[model_idx - 1].model;
|
||||
|
||||
while (model_idx < numcpus) {
|
||||
model = uv__strndup(inferred_model, strlen(inferred_model));
|
||||
if (model == NULL)
|
||||
return -ENOMEM;
|
||||
ci[model_idx++].model = model;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int read_times(FILE* statfile_fp,
|
||||
unsigned int numcpus,
|
||||
uv_cpu_info_t* ci) {
|
||||
unsigned long clock_ticks;
|
||||
struct uv_cpu_times_s ts;
|
||||
unsigned long user;
|
||||
unsigned long nice;
|
||||
unsigned long sys;
|
||||
unsigned long idle;
|
||||
unsigned long dummy;
|
||||
unsigned long irq;
|
||||
unsigned int num;
|
||||
unsigned int len;
|
||||
char buf[1024];
|
||||
|
||||
clock_ticks = sysconf(_SC_CLK_TCK);
|
||||
assert(clock_ticks != (unsigned long) -1);
|
||||
assert(clock_ticks != 0);
|
||||
|
||||
rewind(statfile_fp);
|
||||
|
||||
if (!fgets(buf, sizeof(buf), statfile_fp))
|
||||
abort();
|
||||
|
||||
num = 0;
|
||||
|
||||
while (fgets(buf, sizeof(buf), statfile_fp)) {
|
||||
if (num >= numcpus)
|
||||
break;
|
||||
|
||||
if (strncmp(buf, "cpu", 3))
|
||||
break;
|
||||
|
||||
/* skip "cpu<num> " marker */
|
||||
{
|
||||
unsigned int n;
|
||||
int r = sscanf(buf, "cpu%u ", &n);
|
||||
assert(r == 1);
|
||||
(void) r; /* silence build warning */
|
||||
for (len = sizeof("cpu0"); n /= 10; len++);
|
||||
}
|
||||
|
||||
/* Line contains user, nice, system, idle, iowait, irq, softirq, steal,
|
||||
* guest, guest_nice but we're only interested in the first four + irq.
|
||||
*
|
||||
* Don't use %*s to skip fields or %ll to read straight into the uint64_t
|
||||
* fields, they're not allowed in C89 mode.
|
||||
*/
|
||||
if (6 != sscanf(buf + len,
|
||||
"%lu %lu %lu %lu %lu %lu",
|
||||
&user,
|
||||
&nice,
|
||||
&sys,
|
||||
&idle,
|
||||
&dummy,
|
||||
&irq))
|
||||
abort();
|
||||
|
||||
ts.user = clock_ticks * user;
|
||||
ts.nice = clock_ticks * nice;
|
||||
ts.sys = clock_ticks * sys;
|
||||
ts.idle = clock_ticks * idle;
|
||||
ts.irq = clock_ticks * irq;
|
||||
ci[num++].cpu_times = ts;
|
||||
}
|
||||
assert(num == numcpus);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static unsigned long read_cpufreq(unsigned int cpunum) {
|
||||
unsigned long val;
|
||||
char buf[1024];
|
||||
FILE* fp;
|
||||
|
||||
snprintf(buf,
|
||||
sizeof(buf),
|
||||
"/sys/devices/system/cpu/cpu%u/cpufreq/scaling_cur_freq",
|
||||
cpunum);
|
||||
|
||||
fp = uv__open_file(buf);
|
||||
if (fp == NULL)
|
||||
return 0;
|
||||
|
||||
if (fscanf(fp, "%lu", &val) != 1)
|
||||
val = 0;
|
||||
|
||||
fclose(fp);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
|
||||
void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
uv__free(cpu_infos[i].model);
|
||||
}
|
||||
|
||||
uv__free(cpu_infos);
|
||||
}
|
||||
|
||||
static int uv__ifaddr_exclude(struct ifaddrs *ent, int exclude_type) {
|
||||
if (!((ent->ifa_flags & IFF_UP) && (ent->ifa_flags & IFF_RUNNING)))
|
||||
return 1;
|
||||
if (ent->ifa_addr == NULL)
|
||||
return 1;
|
||||
/*
|
||||
* On Linux getifaddrs returns information related to the raw underlying
|
||||
* devices. We're not interested in this information yet.
|
||||
*/
|
||||
if (ent->ifa_addr->sa_family == PF_PACKET)
|
||||
return exclude_type;
|
||||
return !exclude_type;
|
||||
}
|
||||
|
||||
int uv_interface_addresses(uv_interface_address_t** addresses,
|
||||
int* count) {
|
||||
#ifndef HAVE_IFADDRS_H
|
||||
return -ENOSYS;
|
||||
#else
|
||||
struct ifaddrs *addrs, *ent;
|
||||
uv_interface_address_t* address;
|
||||
int i;
|
||||
struct sockaddr_ll *sll;
|
||||
|
||||
if (getifaddrs(&addrs))
|
||||
return -errno;
|
||||
|
||||
*count = 0;
|
||||
*addresses = NULL;
|
||||
|
||||
/* Count the number of interfaces */
|
||||
for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
|
||||
if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFADDR))
|
||||
continue;
|
||||
|
||||
(*count)++;
|
||||
}
|
||||
|
||||
if (*count == 0)
|
||||
return 0;
|
||||
|
||||
*addresses = uv__malloc(*count * sizeof(**addresses));
|
||||
if (!(*addresses)) {
|
||||
freeifaddrs(addrs);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
address = *addresses;
|
||||
|
||||
for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
|
||||
if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFADDR))
|
||||
continue;
|
||||
|
||||
address->name = uv__strdup(ent->ifa_name);
|
||||
|
||||
if (ent->ifa_addr->sa_family == AF_INET6) {
|
||||
address->address.address6 = *((struct sockaddr_in6*) ent->ifa_addr);
|
||||
} else {
|
||||
address->address.address4 = *((struct sockaddr_in*) ent->ifa_addr);
|
||||
}
|
||||
|
||||
if (ent->ifa_netmask->sa_family == AF_INET6) {
|
||||
address->netmask.netmask6 = *((struct sockaddr_in6*) ent->ifa_netmask);
|
||||
} else {
|
||||
address->netmask.netmask4 = *((struct sockaddr_in*) ent->ifa_netmask);
|
||||
}
|
||||
|
||||
address->is_internal = !!(ent->ifa_flags & IFF_LOOPBACK);
|
||||
|
||||
address++;
|
||||
}
|
||||
|
||||
/* Fill in physical addresses for each interface */
|
||||
for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
|
||||
if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFPHYS))
|
||||
continue;
|
||||
|
||||
address = *addresses;
|
||||
|
||||
for (i = 0; i < (*count); i++) {
|
||||
if (strcmp(address->name, ent->ifa_name) == 0) {
|
||||
sll = (struct sockaddr_ll*)ent->ifa_addr;
|
||||
memcpy(address->phys_addr, sll->sll_addr, sizeof(address->phys_addr));
|
||||
}
|
||||
address++;
|
||||
}
|
||||
}
|
||||
|
||||
freeifaddrs(addrs);
|
||||
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void uv_free_interface_addresses(uv_interface_address_t* addresses,
|
||||
int count) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
uv__free(addresses[i].name);
|
||||
}
|
||||
|
||||
uv__free(addresses);
|
||||
}
|
||||
|
||||
|
||||
void uv__set_process_title(const char* title) {
|
||||
#if defined(PR_SET_NAME)
|
||||
prctl(PR_SET_NAME, title); /* Only copies first 16 characters. */
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,352 @@
|
||||
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "uv.h"
|
||||
#include "tree.h"
|
||||
#include "internal.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
struct watcher_list {
|
||||
RB_ENTRY(watcher_list) entry;
|
||||
QUEUE watchers;
|
||||
int iterating;
|
||||
char* path;
|
||||
int wd;
|
||||
};
|
||||
|
||||
struct watcher_root {
|
||||
struct watcher_list* rbh_root;
|
||||
};
|
||||
#define CAST(p) ((struct watcher_root*)(p))
|
||||
|
||||
|
||||
static int compare_watchers(const struct watcher_list* a,
|
||||
const struct watcher_list* b) {
|
||||
if (a->wd < b->wd) return -1;
|
||||
if (a->wd > b->wd) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
RB_GENERATE_STATIC(watcher_root, watcher_list, entry, compare_watchers)
|
||||
|
||||
|
||||
static void uv__inotify_read(uv_loop_t* loop,
|
||||
uv__io_t* w,
|
||||
unsigned int revents);
|
||||
|
||||
static void maybe_free_watcher_list(struct watcher_list* w,
|
||||
uv_loop_t* loop);
|
||||
|
||||
static int new_inotify_fd(void) {
|
||||
int err;
|
||||
int fd;
|
||||
|
||||
fd = uv__inotify_init1(UV__IN_NONBLOCK | UV__IN_CLOEXEC);
|
||||
if (fd != -1)
|
||||
return fd;
|
||||
|
||||
if (errno != ENOSYS)
|
||||
return -errno;
|
||||
|
||||
fd = uv__inotify_init();
|
||||
if (fd == -1)
|
||||
return -errno;
|
||||
|
||||
err = uv__cloexec(fd, 1);
|
||||
if (err == 0)
|
||||
err = uv__nonblock(fd, 1);
|
||||
|
||||
if (err) {
|
||||
uv__close(fd);
|
||||
return err;
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
|
||||
static int init_inotify(uv_loop_t* loop) {
|
||||
int err;
|
||||
|
||||
if (loop->inotify_fd != -1)
|
||||
return 0;
|
||||
|
||||
err = new_inotify_fd();
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
loop->inotify_fd = err;
|
||||
uv__io_init(&loop->inotify_read_watcher, uv__inotify_read, loop->inotify_fd);
|
||||
uv__io_start(loop, &loop->inotify_read_watcher, POLLIN);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv__inotify_fork(uv_loop_t* loop, void* old_watchers) {
|
||||
/* Open the inotify_fd, and re-arm all the inotify watchers. */
|
||||
int err;
|
||||
struct watcher_list* tmp_watcher_list_iter;
|
||||
struct watcher_list* watcher_list;
|
||||
struct watcher_list tmp_watcher_list;
|
||||
QUEUE queue;
|
||||
QUEUE* q;
|
||||
uv_fs_event_t* handle;
|
||||
char* tmp_path;
|
||||
|
||||
if (old_watchers != NULL) {
|
||||
/* We must restore the old watcher list to be able to close items
|
||||
* out of it.
|
||||
*/
|
||||
loop->inotify_watchers = old_watchers;
|
||||
|
||||
QUEUE_INIT(&tmp_watcher_list.watchers);
|
||||
/* Note that the queue we use is shared with the start and stop()
|
||||
* functions, making QUEUE_FOREACH unsafe to use. So we use the
|
||||
* QUEUE_MOVE trick to safely iterate. Also don't free the watcher
|
||||
* list until we're done iterating. c.f. uv__inotify_read.
|
||||
*/
|
||||
RB_FOREACH_SAFE(watcher_list, watcher_root,
|
||||
CAST(&old_watchers), tmp_watcher_list_iter) {
|
||||
watcher_list->iterating = 1;
|
||||
QUEUE_MOVE(&watcher_list->watchers, &queue);
|
||||
while (!QUEUE_EMPTY(&queue)) {
|
||||
q = QUEUE_HEAD(&queue);
|
||||
handle = QUEUE_DATA(q, uv_fs_event_t, watchers);
|
||||
/* It's critical to keep a copy of path here, because it
|
||||
* will be set to NULL by stop() and then deallocated by
|
||||
* maybe_free_watcher_list
|
||||
*/
|
||||
tmp_path = uv__strdup(handle->path);
|
||||
assert(tmp_path != NULL);
|
||||
QUEUE_REMOVE(q);
|
||||
QUEUE_INSERT_TAIL(&watcher_list->watchers, q);
|
||||
uv_fs_event_stop(handle);
|
||||
|
||||
QUEUE_INSERT_TAIL(&tmp_watcher_list.watchers, &handle->watchers);
|
||||
handle->path = tmp_path;
|
||||
}
|
||||
watcher_list->iterating = 0;
|
||||
maybe_free_watcher_list(watcher_list, loop);
|
||||
}
|
||||
|
||||
QUEUE_MOVE(&tmp_watcher_list.watchers, &queue);
|
||||
while (!QUEUE_EMPTY(&queue)) {
|
||||
q = QUEUE_HEAD(&queue);
|
||||
QUEUE_REMOVE(q);
|
||||
handle = QUEUE_DATA(q, uv_fs_event_t, watchers);
|
||||
tmp_path = handle->path;
|
||||
handle->path = NULL;
|
||||
err = uv_fs_event_start(handle, handle->cb, tmp_path, 0);
|
||||
uv__free(tmp_path);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static struct watcher_list* find_watcher(uv_loop_t* loop, int wd) {
|
||||
struct watcher_list w;
|
||||
w.wd = wd;
|
||||
return RB_FIND(watcher_root, CAST(&loop->inotify_watchers), &w);
|
||||
}
|
||||
|
||||
static void maybe_free_watcher_list(struct watcher_list* w, uv_loop_t* loop) {
|
||||
/* if the watcher_list->watchers is being iterated over, we can't free it. */
|
||||
if ((!w->iterating) && QUEUE_EMPTY(&w->watchers)) {
|
||||
/* No watchers left for this path. Clean up. */
|
||||
RB_REMOVE(watcher_root, CAST(&loop->inotify_watchers), w);
|
||||
uv__inotify_rm_watch(loop->inotify_fd, w->wd);
|
||||
uv__free(w);
|
||||
}
|
||||
}
|
||||
|
||||
static void uv__inotify_read(uv_loop_t* loop,
|
||||
uv__io_t* dummy,
|
||||
unsigned int events) {
|
||||
const struct uv__inotify_event* e;
|
||||
struct watcher_list* w;
|
||||
uv_fs_event_t* h;
|
||||
QUEUE queue;
|
||||
QUEUE* q;
|
||||
const char* path;
|
||||
ssize_t size;
|
||||
const char *p;
|
||||
/* needs to be large enough for sizeof(inotify_event) + strlen(path) */
|
||||
char buf[4096];
|
||||
|
||||
while (1) {
|
||||
do
|
||||
size = read(loop->inotify_fd, buf, sizeof(buf));
|
||||
while (size == -1 && errno == EINTR);
|
||||
|
||||
if (size == -1) {
|
||||
assert(errno == EAGAIN || errno == EWOULDBLOCK);
|
||||
break;
|
||||
}
|
||||
|
||||
assert(size > 0); /* pre-2.6.21 thing, size=0 == read buffer too small */
|
||||
|
||||
/* Now we have one or more inotify_event structs. */
|
||||
for (p = buf; p < buf + size; p += sizeof(*e) + e->len) {
|
||||
e = (const struct uv__inotify_event*)p;
|
||||
|
||||
events = 0;
|
||||
if (e->mask & (UV__IN_ATTRIB|UV__IN_MODIFY))
|
||||
events |= UV_CHANGE;
|
||||
if (e->mask & ~(UV__IN_ATTRIB|UV__IN_MODIFY))
|
||||
events |= UV_RENAME;
|
||||
|
||||
w = find_watcher(loop, e->wd);
|
||||
if (w == NULL)
|
||||
continue; /* Stale event, no watchers left. */
|
||||
|
||||
/* inotify does not return the filename when monitoring a single file
|
||||
* for modifications. Repurpose the filename for API compatibility.
|
||||
* I'm not convinced this is a good thing, maybe it should go.
|
||||
*/
|
||||
path = e->len ? (const char*) (e + 1) : uv__basename_r(w->path);
|
||||
|
||||
/* We're about to iterate over the queue and call user's callbacks.
|
||||
* What can go wrong?
|
||||
* A callback could call uv_fs_event_stop()
|
||||
* and the queue can change under our feet.
|
||||
* So, we use QUEUE_MOVE() trick to safely iterate over the queue.
|
||||
* And we don't free the watcher_list until we're done iterating.
|
||||
*
|
||||
* First,
|
||||
* tell uv_fs_event_stop() (that could be called from a user's callback)
|
||||
* not to free watcher_list.
|
||||
*/
|
||||
w->iterating = 1;
|
||||
QUEUE_MOVE(&w->watchers, &queue);
|
||||
while (!QUEUE_EMPTY(&queue)) {
|
||||
q = QUEUE_HEAD(&queue);
|
||||
h = QUEUE_DATA(q, uv_fs_event_t, watchers);
|
||||
|
||||
QUEUE_REMOVE(q);
|
||||
QUEUE_INSERT_TAIL(&w->watchers, q);
|
||||
|
||||
h->cb(h, path, events, 0);
|
||||
}
|
||||
/* done iterating, time to (maybe) free empty watcher_list */
|
||||
w->iterating = 0;
|
||||
maybe_free_watcher_list(w, loop);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int uv_fs_event_init(uv_loop_t* loop, uv_fs_event_t* handle) {
|
||||
uv__handle_init(loop, (uv_handle_t*)handle, UV_FS_EVENT);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv_fs_event_start(uv_fs_event_t* handle,
|
||||
uv_fs_event_cb cb,
|
||||
const char* path,
|
||||
unsigned int flags) {
|
||||
struct watcher_list* w;
|
||||
int events;
|
||||
int err;
|
||||
int wd;
|
||||
|
||||
if (uv__is_active(handle))
|
||||
return -EINVAL;
|
||||
|
||||
err = init_inotify(handle->loop);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
events = UV__IN_ATTRIB
|
||||
| UV__IN_CREATE
|
||||
| UV__IN_MODIFY
|
||||
| UV__IN_DELETE
|
||||
| UV__IN_DELETE_SELF
|
||||
| UV__IN_MOVE_SELF
|
||||
| UV__IN_MOVED_FROM
|
||||
| UV__IN_MOVED_TO;
|
||||
|
||||
wd = uv__inotify_add_watch(handle->loop->inotify_fd, path, events);
|
||||
if (wd == -1)
|
||||
return -errno;
|
||||
|
||||
w = find_watcher(handle->loop, wd);
|
||||
if (w)
|
||||
goto no_insert;
|
||||
|
||||
w = uv__malloc(sizeof(*w) + strlen(path) + 1);
|
||||
if (w == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
w->wd = wd;
|
||||
w->path = strcpy((char*)(w + 1), path);
|
||||
QUEUE_INIT(&w->watchers);
|
||||
w->iterating = 0;
|
||||
RB_INSERT(watcher_root, CAST(&handle->loop->inotify_watchers), w);
|
||||
|
||||
no_insert:
|
||||
uv__handle_start(handle);
|
||||
QUEUE_INSERT_TAIL(&w->watchers, &handle->watchers);
|
||||
handle->path = w->path;
|
||||
handle->cb = cb;
|
||||
handle->wd = wd;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv_fs_event_stop(uv_fs_event_t* handle) {
|
||||
struct watcher_list* w;
|
||||
|
||||
if (!uv__is_active(handle))
|
||||
return 0;
|
||||
|
||||
w = find_watcher(handle->loop, handle->wd);
|
||||
assert(w != NULL);
|
||||
|
||||
handle->wd = -1;
|
||||
handle->path = NULL;
|
||||
uv__handle_stop(handle);
|
||||
QUEUE_REMOVE(&handle->watchers);
|
||||
|
||||
maybe_free_watcher_list(w, handle->loop);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void uv__fs_event_close(uv_fs_event_t* handle) {
|
||||
uv_fs_event_stop(handle);
|
||||
}
|
||||
@@ -0,0 +1,471 @@
|
||||
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "linux-syscalls.h"
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/types.h>
|
||||
#include <errno.h>
|
||||
|
||||
#if defined(__has_feature)
|
||||
# if __has_feature(memory_sanitizer)
|
||||
# define MSAN_ACTIVE 1
|
||||
# include <sanitizer/msan_interface.h>
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if defined(__i386__)
|
||||
# ifndef __NR_socketcall
|
||||
# define __NR_socketcall 102
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if defined(__arm__)
|
||||
# if defined(__thumb__) || defined(__ARM_EABI__)
|
||||
# define UV_SYSCALL_BASE 0
|
||||
# else
|
||||
# define UV_SYSCALL_BASE 0x900000
|
||||
# endif
|
||||
#endif /* __arm__ */
|
||||
|
||||
#ifndef __NR_accept4
|
||||
# if defined(__x86_64__)
|
||||
# define __NR_accept4 288
|
||||
# elif defined(__i386__)
|
||||
/* Nothing. Handled through socketcall(). */
|
||||
# elif defined(__arm__)
|
||||
# define __NR_accept4 (UV_SYSCALL_BASE + 366)
|
||||
# endif
|
||||
#endif /* __NR_accept4 */
|
||||
|
||||
#ifndef __NR_eventfd
|
||||
# if defined(__x86_64__)
|
||||
# define __NR_eventfd 284
|
||||
# elif defined(__i386__)
|
||||
# define __NR_eventfd 323
|
||||
# elif defined(__arm__)
|
||||
# define __NR_eventfd (UV_SYSCALL_BASE + 351)
|
||||
# endif
|
||||
#endif /* __NR_eventfd */
|
||||
|
||||
#ifndef __NR_eventfd2
|
||||
# if defined(__x86_64__)
|
||||
# define __NR_eventfd2 290
|
||||
# elif defined(__i386__)
|
||||
# define __NR_eventfd2 328
|
||||
# elif defined(__arm__)
|
||||
# define __NR_eventfd2 (UV_SYSCALL_BASE + 356)
|
||||
# endif
|
||||
#endif /* __NR_eventfd2 */
|
||||
|
||||
#ifndef __NR_epoll_create
|
||||
# if defined(__x86_64__)
|
||||
# define __NR_epoll_create 213
|
||||
# elif defined(__i386__)
|
||||
# define __NR_epoll_create 254
|
||||
# elif defined(__arm__)
|
||||
# define __NR_epoll_create (UV_SYSCALL_BASE + 250)
|
||||
# endif
|
||||
#endif /* __NR_epoll_create */
|
||||
|
||||
#ifndef __NR_epoll_create1
|
||||
# if defined(__x86_64__)
|
||||
# define __NR_epoll_create1 291
|
||||
# elif defined(__i386__)
|
||||
# define __NR_epoll_create1 329
|
||||
# elif defined(__arm__)
|
||||
# define __NR_epoll_create1 (UV_SYSCALL_BASE + 357)
|
||||
# endif
|
||||
#endif /* __NR_epoll_create1 */
|
||||
|
||||
#ifndef __NR_epoll_ctl
|
||||
# if defined(__x86_64__)
|
||||
# define __NR_epoll_ctl 233 /* used to be 214 */
|
||||
# elif defined(__i386__)
|
||||
# define __NR_epoll_ctl 255
|
||||
# elif defined(__arm__)
|
||||
# define __NR_epoll_ctl (UV_SYSCALL_BASE + 251)
|
||||
# endif
|
||||
#endif /* __NR_epoll_ctl */
|
||||
|
||||
#ifndef __NR_epoll_wait
|
||||
# if defined(__x86_64__)
|
||||
# define __NR_epoll_wait 232 /* used to be 215 */
|
||||
# elif defined(__i386__)
|
||||
# define __NR_epoll_wait 256
|
||||
# elif defined(__arm__)
|
||||
# define __NR_epoll_wait (UV_SYSCALL_BASE + 252)
|
||||
# endif
|
||||
#endif /* __NR_epoll_wait */
|
||||
|
||||
#ifndef __NR_epoll_pwait
|
||||
# if defined(__x86_64__)
|
||||
# define __NR_epoll_pwait 281
|
||||
# elif defined(__i386__)
|
||||
# define __NR_epoll_pwait 319
|
||||
# elif defined(__arm__)
|
||||
# define __NR_epoll_pwait (UV_SYSCALL_BASE + 346)
|
||||
# endif
|
||||
#endif /* __NR_epoll_pwait */
|
||||
|
||||
#ifndef __NR_inotify_init
|
||||
# if defined(__x86_64__)
|
||||
# define __NR_inotify_init 253
|
||||
# elif defined(__i386__)
|
||||
# define __NR_inotify_init 291
|
||||
# elif defined(__arm__)
|
||||
# define __NR_inotify_init (UV_SYSCALL_BASE + 316)
|
||||
# endif
|
||||
#endif /* __NR_inotify_init */
|
||||
|
||||
#ifndef __NR_inotify_init1
|
||||
# if defined(__x86_64__)
|
||||
# define __NR_inotify_init1 294
|
||||
# elif defined(__i386__)
|
||||
# define __NR_inotify_init1 332
|
||||
# elif defined(__arm__)
|
||||
# define __NR_inotify_init1 (UV_SYSCALL_BASE + 360)
|
||||
# endif
|
||||
#endif /* __NR_inotify_init1 */
|
||||
|
||||
#ifndef __NR_inotify_add_watch
|
||||
# if defined(__x86_64__)
|
||||
# define __NR_inotify_add_watch 254
|
||||
# elif defined(__i386__)
|
||||
# define __NR_inotify_add_watch 292
|
||||
# elif defined(__arm__)
|
||||
# define __NR_inotify_add_watch (UV_SYSCALL_BASE + 317)
|
||||
# endif
|
||||
#endif /* __NR_inotify_add_watch */
|
||||
|
||||
#ifndef __NR_inotify_rm_watch
|
||||
# if defined(__x86_64__)
|
||||
# define __NR_inotify_rm_watch 255
|
||||
# elif defined(__i386__)
|
||||
# define __NR_inotify_rm_watch 293
|
||||
# elif defined(__arm__)
|
||||
# define __NR_inotify_rm_watch (UV_SYSCALL_BASE + 318)
|
||||
# endif
|
||||
#endif /* __NR_inotify_rm_watch */
|
||||
|
||||
#ifndef __NR_pipe2
|
||||
# if defined(__x86_64__)
|
||||
# define __NR_pipe2 293
|
||||
# elif defined(__i386__)
|
||||
# define __NR_pipe2 331
|
||||
# elif defined(__arm__)
|
||||
# define __NR_pipe2 (UV_SYSCALL_BASE + 359)
|
||||
# endif
|
||||
#endif /* __NR_pipe2 */
|
||||
|
||||
#ifndef __NR_recvmmsg
|
||||
# if defined(__x86_64__)
|
||||
# define __NR_recvmmsg 299
|
||||
# elif defined(__i386__)
|
||||
# define __NR_recvmmsg 337
|
||||
# elif defined(__arm__)
|
||||
# define __NR_recvmmsg (UV_SYSCALL_BASE + 365)
|
||||
# endif
|
||||
#endif /* __NR_recvmsg */
|
||||
|
||||
#ifndef __NR_sendmmsg
|
||||
# if defined(__x86_64__)
|
||||
# define __NR_sendmmsg 307
|
||||
# elif defined(__i386__)
|
||||
# define __NR_sendmmsg 345
|
||||
# elif defined(__arm__)
|
||||
# define __NR_sendmmsg (UV_SYSCALL_BASE + 374)
|
||||
# endif
|
||||
#endif /* __NR_sendmmsg */
|
||||
|
||||
#ifndef __NR_utimensat
|
||||
# if defined(__x86_64__)
|
||||
# define __NR_utimensat 280
|
||||
# elif defined(__i386__)
|
||||
# define __NR_utimensat 320
|
||||
# elif defined(__arm__)
|
||||
# define __NR_utimensat (UV_SYSCALL_BASE + 348)
|
||||
# endif
|
||||
#endif /* __NR_utimensat */
|
||||
|
||||
#ifndef __NR_preadv
|
||||
# if defined(__x86_64__)
|
||||
# define __NR_preadv 295
|
||||
# elif defined(__i386__)
|
||||
# define __NR_preadv 333
|
||||
# elif defined(__arm__)
|
||||
# define __NR_preadv (UV_SYSCALL_BASE + 361)
|
||||
# endif
|
||||
#endif /* __NR_preadv */
|
||||
|
||||
#ifndef __NR_pwritev
|
||||
# if defined(__x86_64__)
|
||||
# define __NR_pwritev 296
|
||||
# elif defined(__i386__)
|
||||
# define __NR_pwritev 334
|
||||
# elif defined(__arm__)
|
||||
# define __NR_pwritev (UV_SYSCALL_BASE + 362)
|
||||
# endif
|
||||
#endif /* __NR_pwritev */
|
||||
|
||||
#ifndef __NR_dup3
|
||||
# if defined(__x86_64__)
|
||||
# define __NR_dup3 292
|
||||
# elif defined(__i386__)
|
||||
# define __NR_dup3 330
|
||||
# elif defined(__arm__)
|
||||
# define __NR_dup3 (UV_SYSCALL_BASE + 358)
|
||||
# endif
|
||||
#endif /* __NR_pwritev */
|
||||
|
||||
|
||||
int uv__accept4(int fd, struct sockaddr* addr, socklen_t* addrlen, int flags) {
|
||||
#if defined(__i386__)
|
||||
unsigned long args[4];
|
||||
int r;
|
||||
|
||||
args[0] = (unsigned long) fd;
|
||||
args[1] = (unsigned long) addr;
|
||||
args[2] = (unsigned long) addrlen;
|
||||
args[3] = (unsigned long) flags;
|
||||
|
||||
r = syscall(__NR_socketcall, 18 /* SYS_ACCEPT4 */, args);
|
||||
|
||||
/* socketcall() raises EINVAL when SYS_ACCEPT4 is not supported but so does
|
||||
* a bad flags argument. Try to distinguish between the two cases.
|
||||
*/
|
||||
if (r == -1)
|
||||
if (errno == EINVAL)
|
||||
if ((flags & ~(UV__SOCK_CLOEXEC|UV__SOCK_NONBLOCK)) == 0)
|
||||
errno = ENOSYS;
|
||||
|
||||
return r;
|
||||
#elif defined(__NR_accept4)
|
||||
return syscall(__NR_accept4, fd, addr, addrlen, flags);
|
||||
#else
|
||||
return errno = ENOSYS, -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
int uv__eventfd(unsigned int count) {
|
||||
#if defined(__NR_eventfd)
|
||||
return syscall(__NR_eventfd, count);
|
||||
#else
|
||||
return errno = ENOSYS, -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
int uv__eventfd2(unsigned int count, int flags) {
|
||||
#if defined(__NR_eventfd2)
|
||||
return syscall(__NR_eventfd2, count, flags);
|
||||
#else
|
||||
return errno = ENOSYS, -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
int uv__epoll_create(int size) {
|
||||
#if defined(__NR_epoll_create)
|
||||
return syscall(__NR_epoll_create, size);
|
||||
#else
|
||||
return errno = ENOSYS, -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
int uv__epoll_create1(int flags) {
|
||||
#if defined(__NR_epoll_create1)
|
||||
return syscall(__NR_epoll_create1, flags);
|
||||
#else
|
||||
return errno = ENOSYS, -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
int uv__epoll_ctl(int epfd, int op, int fd, struct uv__epoll_event* events) {
|
||||
#if defined(__NR_epoll_ctl)
|
||||
return syscall(__NR_epoll_ctl, epfd, op, fd, events);
|
||||
#else
|
||||
return errno = ENOSYS, -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
int uv__epoll_wait(int epfd,
|
||||
struct uv__epoll_event* events,
|
||||
int nevents,
|
||||
int timeout) {
|
||||
#if defined(__NR_epoll_wait)
|
||||
int result;
|
||||
result = syscall(__NR_epoll_wait, epfd, events, nevents, timeout);
|
||||
#if MSAN_ACTIVE
|
||||
if (result > 0)
|
||||
__msan_unpoison(events, sizeof(events[0]) * result);
|
||||
#endif
|
||||
return result;
|
||||
#else
|
||||
return errno = ENOSYS, -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
int uv__epoll_pwait(int epfd,
|
||||
struct uv__epoll_event* events,
|
||||
int nevents,
|
||||
int timeout,
|
||||
uint64_t sigmask) {
|
||||
#if defined(__NR_epoll_pwait)
|
||||
int result;
|
||||
result = syscall(__NR_epoll_pwait,
|
||||
epfd,
|
||||
events,
|
||||
nevents,
|
||||
timeout,
|
||||
&sigmask,
|
||||
sizeof(sigmask));
|
||||
#if MSAN_ACTIVE
|
||||
if (result > 0)
|
||||
__msan_unpoison(events, sizeof(events[0]) * result);
|
||||
#endif
|
||||
return result;
|
||||
#else
|
||||
return errno = ENOSYS, -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
int uv__inotify_init(void) {
|
||||
#if defined(__NR_inotify_init)
|
||||
return syscall(__NR_inotify_init);
|
||||
#else
|
||||
return errno = ENOSYS, -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
int uv__inotify_init1(int flags) {
|
||||
#if defined(__NR_inotify_init1)
|
||||
return syscall(__NR_inotify_init1, flags);
|
||||
#else
|
||||
return errno = ENOSYS, -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
int uv__inotify_add_watch(int fd, const char* path, uint32_t mask) {
|
||||
#if defined(__NR_inotify_add_watch)
|
||||
return syscall(__NR_inotify_add_watch, fd, path, mask);
|
||||
#else
|
||||
return errno = ENOSYS, -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
int uv__inotify_rm_watch(int fd, int32_t wd) {
|
||||
#if defined(__NR_inotify_rm_watch)
|
||||
return syscall(__NR_inotify_rm_watch, fd, wd);
|
||||
#else
|
||||
return errno = ENOSYS, -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
int uv__pipe2(int pipefd[2], int flags) {
|
||||
#if defined(__NR_pipe2)
|
||||
int result;
|
||||
result = syscall(__NR_pipe2, pipefd, flags);
|
||||
#if MSAN_ACTIVE
|
||||
if (!result)
|
||||
__msan_unpoison(pipefd, sizeof(int[2]));
|
||||
#endif
|
||||
return result;
|
||||
#else
|
||||
return errno = ENOSYS, -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
int uv__sendmmsg(int fd,
|
||||
struct uv__mmsghdr* mmsg,
|
||||
unsigned int vlen,
|
||||
unsigned int flags) {
|
||||
#if defined(__NR_sendmmsg)
|
||||
return syscall(__NR_sendmmsg, fd, mmsg, vlen, flags);
|
||||
#else
|
||||
return errno = ENOSYS, -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
int uv__recvmmsg(int fd,
|
||||
struct uv__mmsghdr* mmsg,
|
||||
unsigned int vlen,
|
||||
unsigned int flags,
|
||||
struct timespec* timeout) {
|
||||
#if defined(__NR_recvmmsg)
|
||||
return syscall(__NR_recvmmsg, fd, mmsg, vlen, flags, timeout);
|
||||
#else
|
||||
return errno = ENOSYS, -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
int uv__utimesat(int dirfd,
|
||||
const char* path,
|
||||
const struct timespec times[2],
|
||||
int flags)
|
||||
{
|
||||
#if defined(__NR_utimensat)
|
||||
return syscall(__NR_utimensat, dirfd, path, times, flags);
|
||||
#else
|
||||
return errno = ENOSYS, -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
ssize_t uv__preadv(int fd, const struct iovec *iov, int iovcnt, int64_t offset) {
|
||||
#if defined(__NR_preadv)
|
||||
return syscall(__NR_preadv, fd, iov, iovcnt, (long)offset, (long)(offset >> 32));
|
||||
#else
|
||||
return errno = ENOSYS, -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
ssize_t uv__pwritev(int fd, const struct iovec *iov, int iovcnt, int64_t offset) {
|
||||
#if defined(__NR_pwritev)
|
||||
return syscall(__NR_pwritev, fd, iov, iovcnt, (long)offset, (long)(offset >> 32));
|
||||
#else
|
||||
return errno = ENOSYS, -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
int uv__dup3(int oldfd, int newfd, int flags) {
|
||||
#if defined(__NR_dup3)
|
||||
return syscall(__NR_dup3, oldfd, newfd, flags);
|
||||
#else
|
||||
return errno = ENOSYS, -1;
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef UV_LINUX_SYSCALL_H_
|
||||
#define UV_LINUX_SYSCALL_H_
|
||||
|
||||
#undef _GNU_SOURCE
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <stdint.h>
|
||||
#include <signal.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#if defined(__alpha__)
|
||||
# define UV__O_CLOEXEC 0x200000
|
||||
#elif defined(__hppa__)
|
||||
# define UV__O_CLOEXEC 0x200000
|
||||
#elif defined(__sparc__)
|
||||
# define UV__O_CLOEXEC 0x400000
|
||||
#else
|
||||
# define UV__O_CLOEXEC 0x80000
|
||||
#endif
|
||||
|
||||
#if defined(__alpha__)
|
||||
# define UV__O_NONBLOCK 0x4
|
||||
#elif defined(__hppa__)
|
||||
# define UV__O_NONBLOCK O_NONBLOCK
|
||||
#elif defined(__mips__)
|
||||
# define UV__O_NONBLOCK 0x80
|
||||
#elif defined(__sparc__)
|
||||
# define UV__O_NONBLOCK 0x4000
|
||||
#else
|
||||
# define UV__O_NONBLOCK 0x800
|
||||
#endif
|
||||
|
||||
#define UV__EFD_CLOEXEC UV__O_CLOEXEC
|
||||
#define UV__EFD_NONBLOCK UV__O_NONBLOCK
|
||||
|
||||
#define UV__IN_CLOEXEC UV__O_CLOEXEC
|
||||
#define UV__IN_NONBLOCK UV__O_NONBLOCK
|
||||
|
||||
#define UV__SOCK_CLOEXEC UV__O_CLOEXEC
|
||||
#if defined(SOCK_NONBLOCK)
|
||||
# define UV__SOCK_NONBLOCK SOCK_NONBLOCK
|
||||
#else
|
||||
# define UV__SOCK_NONBLOCK UV__O_NONBLOCK
|
||||
#endif
|
||||
|
||||
/* epoll flags */
|
||||
#define UV__EPOLL_CLOEXEC UV__O_CLOEXEC
|
||||
#define UV__EPOLL_CTL_ADD 1
|
||||
#define UV__EPOLL_CTL_DEL 2
|
||||
#define UV__EPOLL_CTL_MOD 3
|
||||
|
||||
/* inotify flags */
|
||||
#define UV__IN_ACCESS 0x001
|
||||
#define UV__IN_MODIFY 0x002
|
||||
#define UV__IN_ATTRIB 0x004
|
||||
#define UV__IN_CLOSE_WRITE 0x008
|
||||
#define UV__IN_CLOSE_NOWRITE 0x010
|
||||
#define UV__IN_OPEN 0x020
|
||||
#define UV__IN_MOVED_FROM 0x040
|
||||
#define UV__IN_MOVED_TO 0x080
|
||||
#define UV__IN_CREATE 0x100
|
||||
#define UV__IN_DELETE 0x200
|
||||
#define UV__IN_DELETE_SELF 0x400
|
||||
#define UV__IN_MOVE_SELF 0x800
|
||||
|
||||
#if defined(__x86_64__)
|
||||
struct uv__epoll_event {
|
||||
uint32_t events;
|
||||
uint64_t data;
|
||||
} __attribute__((packed));
|
||||
#else
|
||||
struct uv__epoll_event {
|
||||
uint32_t events;
|
||||
uint64_t data;
|
||||
};
|
||||
#endif
|
||||
|
||||
struct uv__inotify_event {
|
||||
int32_t wd;
|
||||
uint32_t mask;
|
||||
uint32_t cookie;
|
||||
uint32_t len;
|
||||
/* char name[0]; */
|
||||
};
|
||||
|
||||
struct uv__mmsghdr {
|
||||
struct msghdr msg_hdr;
|
||||
unsigned int msg_len;
|
||||
};
|
||||
|
||||
int uv__accept4(int fd, struct sockaddr* addr, socklen_t* addrlen, int flags);
|
||||
int uv__eventfd(unsigned int count);
|
||||
int uv__epoll_create(int size);
|
||||
int uv__epoll_create1(int flags);
|
||||
int uv__epoll_ctl(int epfd, int op, int fd, struct uv__epoll_event *ev);
|
||||
int uv__epoll_wait(int epfd,
|
||||
struct uv__epoll_event* events,
|
||||
int nevents,
|
||||
int timeout);
|
||||
int uv__epoll_pwait(int epfd,
|
||||
struct uv__epoll_event* events,
|
||||
int nevents,
|
||||
int timeout,
|
||||
uint64_t sigmask);
|
||||
int uv__eventfd2(unsigned int count, int flags);
|
||||
int uv__inotify_init(void);
|
||||
int uv__inotify_init1(int flags);
|
||||
int uv__inotify_add_watch(int fd, const char* path, uint32_t mask);
|
||||
int uv__inotify_rm_watch(int fd, int32_t wd);
|
||||
int uv__pipe2(int pipefd[2], int flags);
|
||||
int uv__recvmmsg(int fd,
|
||||
struct uv__mmsghdr* mmsg,
|
||||
unsigned int vlen,
|
||||
unsigned int flags,
|
||||
struct timespec* timeout);
|
||||
int uv__sendmmsg(int fd,
|
||||
struct uv__mmsghdr* mmsg,
|
||||
unsigned int vlen,
|
||||
unsigned int flags);
|
||||
int uv__utimesat(int dirfd,
|
||||
const char* path,
|
||||
const struct timespec times[2],
|
||||
int flags);
|
||||
ssize_t uv__preadv(int fd, const struct iovec *iov, int iovcnt, int64_t offset);
|
||||
ssize_t uv__pwritev(int fd, const struct iovec *iov, int iovcnt, int64_t offset);
|
||||
int uv__dup3(int oldfd, int newfd, int flags);
|
||||
|
||||
#endif /* UV_LINUX_SYSCALL_H_ */
|
||||
@@ -0,0 +1,68 @@
|
||||
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "uv.h"
|
||||
#include "internal.h"
|
||||
|
||||
#define UV_LOOP_WATCHER_DEFINE(name, type) \
|
||||
int uv_##name##_init(uv_loop_t* loop, uv_##name##_t* handle) { \
|
||||
uv__handle_init(loop, (uv_handle_t*)handle, UV_##type); \
|
||||
handle->name##_cb = NULL; \
|
||||
return 0; \
|
||||
} \
|
||||
\
|
||||
int uv_##name##_start(uv_##name##_t* handle, uv_##name##_cb cb) { \
|
||||
if (uv__is_active(handle)) return 0; \
|
||||
if (cb == NULL) return -EINVAL; \
|
||||
QUEUE_INSERT_HEAD(&handle->loop->name##_handles, &handle->queue); \
|
||||
handle->name##_cb = cb; \
|
||||
uv__handle_start(handle); \
|
||||
return 0; \
|
||||
} \
|
||||
\
|
||||
int uv_##name##_stop(uv_##name##_t* handle) { \
|
||||
if (!uv__is_active(handle)) return 0; \
|
||||
QUEUE_REMOVE(&handle->queue); \
|
||||
uv__handle_stop(handle); \
|
||||
return 0; \
|
||||
} \
|
||||
\
|
||||
void uv__run_##name(uv_loop_t* loop) { \
|
||||
uv_##name##_t* h; \
|
||||
QUEUE queue; \
|
||||
QUEUE* q; \
|
||||
QUEUE_MOVE(&loop->name##_handles, &queue); \
|
||||
while (!QUEUE_EMPTY(&queue)) { \
|
||||
q = QUEUE_HEAD(&queue); \
|
||||
h = QUEUE_DATA(q, uv_##name##_t, queue); \
|
||||
QUEUE_REMOVE(q); \
|
||||
QUEUE_INSERT_TAIL(&loop->name##_handles, q); \
|
||||
h->name##_cb(h); \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
void uv__##name##_close(uv_##name##_t* handle) { \
|
||||
uv_##name##_stop(handle); \
|
||||
}
|
||||
|
||||
UV_LOOP_WATCHER_DEFINE(prepare, PREPARE)
|
||||
UV_LOOP_WATCHER_DEFINE(check, CHECK)
|
||||
UV_LOOP_WATCHER_DEFINE(idle, IDLE)
|
||||
@@ -0,0 +1,193 @@
|
||||
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "uv.h"
|
||||
#include "tree.h"
|
||||
#include "internal.h"
|
||||
#include "heap-inl.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int uv_loop_init(uv_loop_t* loop) {
|
||||
void* saved_data;
|
||||
int err;
|
||||
|
||||
uv__signal_global_once_init();
|
||||
|
||||
saved_data = loop->data;
|
||||
memset(loop, 0, sizeof(*loop));
|
||||
loop->data = saved_data;
|
||||
|
||||
heap_init((struct heap*) &loop->timer_heap);
|
||||
QUEUE_INIT(&loop->wq);
|
||||
QUEUE_INIT(&loop->active_reqs);
|
||||
QUEUE_INIT(&loop->idle_handles);
|
||||
QUEUE_INIT(&loop->async_handles);
|
||||
QUEUE_INIT(&loop->check_handles);
|
||||
QUEUE_INIT(&loop->prepare_handles);
|
||||
QUEUE_INIT(&loop->handle_queue);
|
||||
|
||||
loop->nfds = 0;
|
||||
loop->watchers = NULL;
|
||||
loop->nwatchers = 0;
|
||||
QUEUE_INIT(&loop->pending_queue);
|
||||
QUEUE_INIT(&loop->watcher_queue);
|
||||
|
||||
loop->closing_handles = NULL;
|
||||
uv__update_time(loop);
|
||||
loop->async_io_watcher.fd = -1;
|
||||
loop->async_wfd = -1;
|
||||
loop->signal_pipefd[0] = -1;
|
||||
loop->signal_pipefd[1] = -1;
|
||||
loop->backend_fd = -1;
|
||||
loop->emfile_fd = -1;
|
||||
|
||||
loop->timer_counter = 0;
|
||||
loop->stop_flag = 0;
|
||||
|
||||
err = uv__platform_loop_init(loop);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = uv_signal_init(loop, &loop->child_watcher);
|
||||
if (err)
|
||||
goto fail_signal_init;
|
||||
|
||||
uv__handle_unref(&loop->child_watcher);
|
||||
loop->child_watcher.flags |= UV__HANDLE_INTERNAL;
|
||||
QUEUE_INIT(&loop->process_handles);
|
||||
|
||||
err = uv_rwlock_init(&loop->cloexec_lock);
|
||||
if (err)
|
||||
goto fail_rwlock_init;
|
||||
|
||||
err = uv_mutex_init(&loop->wq_mutex);
|
||||
if (err)
|
||||
goto fail_mutex_init;
|
||||
|
||||
err = uv_async_init(loop, &loop->wq_async, uv__work_done);
|
||||
if (err)
|
||||
goto fail_async_init;
|
||||
|
||||
uv__handle_unref(&loop->wq_async);
|
||||
loop->wq_async.flags |= UV__HANDLE_INTERNAL;
|
||||
|
||||
return 0;
|
||||
|
||||
fail_async_init:
|
||||
uv_mutex_destroy(&loop->wq_mutex);
|
||||
|
||||
fail_mutex_init:
|
||||
uv_rwlock_destroy(&loop->cloexec_lock);
|
||||
|
||||
fail_rwlock_init:
|
||||
uv__signal_loop_cleanup(loop);
|
||||
|
||||
fail_signal_init:
|
||||
uv__platform_loop_delete(loop);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
int uv_loop_fork(uv_loop_t* loop) {
|
||||
int err;
|
||||
unsigned int i;
|
||||
uv__io_t* w;
|
||||
|
||||
err = uv__io_fork(loop);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = uv__async_fork(loop);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = uv__signal_loop_fork(loop);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* Rearm all the watchers that aren't re-queued by the above. */
|
||||
for (i = 0; i < loop->nwatchers; i++) {
|
||||
w = loop->watchers[i];
|
||||
if (w == NULL)
|
||||
continue;
|
||||
|
||||
if (w->pevents != 0 && QUEUE_EMPTY(&w->watcher_queue)) {
|
||||
w->events = 0; /* Force re-registration in uv__io_poll. */
|
||||
QUEUE_INSERT_TAIL(&loop->watcher_queue, &w->watcher_queue);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void uv__loop_close(uv_loop_t* loop) {
|
||||
uv__signal_loop_cleanup(loop);
|
||||
uv__platform_loop_delete(loop);
|
||||
uv__async_stop(loop);
|
||||
|
||||
if (loop->emfile_fd != -1) {
|
||||
uv__close(loop->emfile_fd);
|
||||
loop->emfile_fd = -1;
|
||||
}
|
||||
|
||||
if (loop->backend_fd != -1) {
|
||||
uv__close(loop->backend_fd);
|
||||
loop->backend_fd = -1;
|
||||
}
|
||||
|
||||
uv_mutex_lock(&loop->wq_mutex);
|
||||
assert(QUEUE_EMPTY(&loop->wq) && "thread pool work queue not empty!");
|
||||
assert(!uv__has_active_reqs(loop));
|
||||
uv_mutex_unlock(&loop->wq_mutex);
|
||||
uv_mutex_destroy(&loop->wq_mutex);
|
||||
|
||||
/*
|
||||
* Note that all thread pool stuff is finished at this point and
|
||||
* it is safe to just destroy rw lock
|
||||
*/
|
||||
uv_rwlock_destroy(&loop->cloexec_lock);
|
||||
|
||||
#if 0
|
||||
assert(QUEUE_EMPTY(&loop->pending_queue));
|
||||
assert(QUEUE_EMPTY(&loop->watcher_queue));
|
||||
assert(loop->nfds == 0);
|
||||
#endif
|
||||
|
||||
uv__free(loop->watchers);
|
||||
loop->watchers = NULL;
|
||||
loop->nwatchers = 0;
|
||||
}
|
||||
|
||||
|
||||
int uv__loop_configure(uv_loop_t* loop, uv_loop_option option, va_list ap) {
|
||||
if (option != UV_LOOP_BLOCK_SIGNAL)
|
||||
return UV_ENOSYS;
|
||||
|
||||
if (va_arg(ap, int) != SIGPROF)
|
||||
return UV_EINVAL;
|
||||
|
||||
loop->flags |= UV_LOOP_BLOCK_SIGPROF;
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,272 @@
|
||||
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "uv.h"
|
||||
#include "internal.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <kvm.h>
|
||||
#include <paths.h>
|
||||
#include <unistd.h>
|
||||
#include <time.h>
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <sys/resource.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <uvm/uvm_extern.h>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <time.h>
|
||||
|
||||
static char *process_title;
|
||||
|
||||
|
||||
int uv__platform_loop_init(uv_loop_t* loop) {
|
||||
return uv__kqueue_init(loop);
|
||||
}
|
||||
|
||||
|
||||
void uv__platform_loop_delete(uv_loop_t* loop) {
|
||||
}
|
||||
|
||||
|
||||
void uv_loadavg(double avg[3]) {
|
||||
struct loadavg info;
|
||||
size_t size = sizeof(info);
|
||||
int which[] = {CTL_VM, VM_LOADAVG};
|
||||
|
||||
if (sysctl(which, 2, &info, &size, NULL, 0) == -1) return;
|
||||
|
||||
avg[0] = (double) info.ldavg[0] / info.fscale;
|
||||
avg[1] = (double) info.ldavg[1] / info.fscale;
|
||||
avg[2] = (double) info.ldavg[2] / info.fscale;
|
||||
}
|
||||
|
||||
|
||||
int uv_exepath(char* buffer, size_t* size) {
|
||||
int mib[4];
|
||||
size_t cb;
|
||||
pid_t mypid;
|
||||
|
||||
if (buffer == NULL || size == NULL || *size == 0)
|
||||
return -EINVAL;
|
||||
|
||||
mypid = getpid();
|
||||
mib[0] = CTL_KERN;
|
||||
mib[1] = KERN_PROC_ARGS;
|
||||
mib[2] = mypid;
|
||||
mib[3] = KERN_PROC_ARGV;
|
||||
|
||||
cb = *size;
|
||||
if (sysctl(mib, 4, buffer, &cb, NULL, 0))
|
||||
return -errno;
|
||||
*size = strlen(buffer);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
uint64_t uv_get_free_memory(void) {
|
||||
struct uvmexp info;
|
||||
size_t size = sizeof(info);
|
||||
int which[] = {CTL_VM, VM_UVMEXP};
|
||||
|
||||
if (sysctl(which, 2, &info, &size, NULL, 0))
|
||||
return -errno;
|
||||
|
||||
return (uint64_t) info.free * sysconf(_SC_PAGESIZE);
|
||||
}
|
||||
|
||||
|
||||
uint64_t uv_get_total_memory(void) {
|
||||
#if defined(HW_PHYSMEM64)
|
||||
uint64_t info;
|
||||
int which[] = {CTL_HW, HW_PHYSMEM64};
|
||||
#else
|
||||
unsigned int info;
|
||||
int which[] = {CTL_HW, HW_PHYSMEM};
|
||||
#endif
|
||||
size_t size = sizeof(info);
|
||||
|
||||
if (sysctl(which, 2, &info, &size, NULL, 0))
|
||||
return -errno;
|
||||
|
||||
return (uint64_t) info;
|
||||
}
|
||||
|
||||
|
||||
char** uv_setup_args(int argc, char** argv) {
|
||||
process_title = argc ? uv__strdup(argv[0]) : NULL;
|
||||
return argv;
|
||||
}
|
||||
|
||||
|
||||
int uv_set_process_title(const char* title) {
|
||||
if (process_title) uv__free(process_title);
|
||||
|
||||
process_title = uv__strdup(title);
|
||||
setproctitle("%s", title);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv_get_process_title(char* buffer, size_t size) {
|
||||
size_t len;
|
||||
|
||||
if (buffer == NULL || size == 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (process_title) {
|
||||
len = strlen(process_title) + 1;
|
||||
|
||||
if (size < len)
|
||||
return -ENOBUFS;
|
||||
|
||||
memcpy(buffer, process_title, len);
|
||||
} else {
|
||||
len = 0;
|
||||
}
|
||||
|
||||
buffer[len] = '\0';
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv_resident_set_memory(size_t* rss) {
|
||||
kvm_t *kd = NULL;
|
||||
struct kinfo_proc2 *kinfo = NULL;
|
||||
pid_t pid;
|
||||
int nprocs;
|
||||
int max_size = sizeof(struct kinfo_proc2);
|
||||
int page_size;
|
||||
|
||||
page_size = getpagesize();
|
||||
pid = getpid();
|
||||
|
||||
kd = kvm_open(NULL, NULL, NULL, KVM_NO_FILES, "kvm_open");
|
||||
|
||||
if (kd == NULL) goto error;
|
||||
|
||||
kinfo = kvm_getproc2(kd, KERN_PROC_PID, pid, max_size, &nprocs);
|
||||
if (kinfo == NULL) goto error;
|
||||
|
||||
*rss = kinfo->p_vm_rssize * page_size;
|
||||
|
||||
kvm_close(kd);
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
if (kd) kvm_close(kd);
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
|
||||
int uv_uptime(double* uptime) {
|
||||
time_t now;
|
||||
struct timeval info;
|
||||
size_t size = sizeof(info);
|
||||
static int which[] = {CTL_KERN, KERN_BOOTTIME};
|
||||
|
||||
if (sysctl(which, 2, &info, &size, NULL, 0))
|
||||
return -errno;
|
||||
|
||||
now = time(NULL);
|
||||
|
||||
*uptime = (double)(now - info.tv_sec);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) {
|
||||
unsigned int ticks = (unsigned int)sysconf(_SC_CLK_TCK);
|
||||
unsigned int multiplier = ((uint64_t)1000L / ticks);
|
||||
unsigned int cur = 0;
|
||||
uv_cpu_info_t* cpu_info;
|
||||
u_int64_t* cp_times;
|
||||
char model[512];
|
||||
u_int64_t cpuspeed;
|
||||
int numcpus;
|
||||
size_t size;
|
||||
int i;
|
||||
|
||||
size = sizeof(model);
|
||||
if (sysctlbyname("machdep.cpu_brand", &model, &size, NULL, 0) &&
|
||||
sysctlbyname("hw.model", &model, &size, NULL, 0)) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
size = sizeof(numcpus);
|
||||
if (sysctlbyname("hw.ncpu", &numcpus, &size, NULL, 0))
|
||||
return -errno;
|
||||
*count = numcpus;
|
||||
|
||||
/* Only i386 and amd64 have machdep.tsc_freq */
|
||||
size = sizeof(cpuspeed);
|
||||
if (sysctlbyname("machdep.tsc_freq", &cpuspeed, &size, NULL, 0))
|
||||
cpuspeed = 0;
|
||||
|
||||
size = numcpus * CPUSTATES * sizeof(*cp_times);
|
||||
cp_times = uv__malloc(size);
|
||||
if (cp_times == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
if (sysctlbyname("kern.cp_time", cp_times, &size, NULL, 0))
|
||||
return -errno;
|
||||
|
||||
*cpu_infos = uv__malloc(numcpus * sizeof(**cpu_infos));
|
||||
if (!(*cpu_infos)) {
|
||||
uv__free(cp_times);
|
||||
uv__free(*cpu_infos);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
for (i = 0; i < numcpus; i++) {
|
||||
cpu_info = &(*cpu_infos)[i];
|
||||
cpu_info->cpu_times.user = (uint64_t)(cp_times[CP_USER+cur]) * multiplier;
|
||||
cpu_info->cpu_times.nice = (uint64_t)(cp_times[CP_NICE+cur]) * multiplier;
|
||||
cpu_info->cpu_times.sys = (uint64_t)(cp_times[CP_SYS+cur]) * multiplier;
|
||||
cpu_info->cpu_times.idle = (uint64_t)(cp_times[CP_IDLE+cur]) * multiplier;
|
||||
cpu_info->cpu_times.irq = (uint64_t)(cp_times[CP_INTR+cur]) * multiplier;
|
||||
cpu_info->model = uv__strdup(model);
|
||||
cpu_info->speed = (int)(cpuspeed/(uint64_t) 1e6);
|
||||
cur += CPUSTATES;
|
||||
}
|
||||
uv__free(cp_times);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
uv__free(cpu_infos[i].model);
|
||||
}
|
||||
|
||||
uv__free(cpu_infos);
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/* Copyright libuv project contributors. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "uv.h"
|
||||
#include "internal.h"
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
int uv_fs_event_init(uv_loop_t* loop, uv_fs_event_t* handle) {
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
int uv_fs_event_start(uv_fs_event_t* handle, uv_fs_event_cb cb,
|
||||
const char* filename, unsigned int flags) {
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
int uv_fs_event_stop(uv_fs_event_t* handle) {
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
void uv__fs_event_close(uv_fs_event_t* handle) {
|
||||
UNREACHABLE();
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/* Copyright libuv project contributors. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "uv.h"
|
||||
#include "internal.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <stddef.h>
|
||||
|
||||
char** uv_setup_args(int argc, char** argv) {
|
||||
return argv;
|
||||
}
|
||||
|
||||
int uv_set_process_title(const char* title) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int uv_get_process_title(char* buffer, size_t size) {
|
||||
if (buffer == NULL || size == 0)
|
||||
return -EINVAL;
|
||||
|
||||
buffer[0] = '\0';
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,284 @@
|
||||
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "uv.h"
|
||||
#include "internal.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/sched.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/sysctl.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <paths.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
|
||||
static char *process_title;
|
||||
|
||||
|
||||
int uv__platform_loop_init(uv_loop_t* loop) {
|
||||
return uv__kqueue_init(loop);
|
||||
}
|
||||
|
||||
|
||||
void uv__platform_loop_delete(uv_loop_t* loop) {
|
||||
}
|
||||
|
||||
|
||||
void uv_loadavg(double avg[3]) {
|
||||
struct loadavg info;
|
||||
size_t size = sizeof(info);
|
||||
int which[] = {CTL_VM, VM_LOADAVG};
|
||||
|
||||
if (sysctl(which, 2, &info, &size, NULL, 0) < 0) return;
|
||||
|
||||
avg[0] = (double) info.ldavg[0] / info.fscale;
|
||||
avg[1] = (double) info.ldavg[1] / info.fscale;
|
||||
avg[2] = (double) info.ldavg[2] / info.fscale;
|
||||
}
|
||||
|
||||
|
||||
int uv_exepath(char* buffer, size_t* size) {
|
||||
int mib[4];
|
||||
char **argsbuf = NULL;
|
||||
char **argsbuf_tmp;
|
||||
size_t argsbuf_size = 100U;
|
||||
size_t exepath_size;
|
||||
pid_t mypid;
|
||||
int err;
|
||||
|
||||
if (buffer == NULL || size == NULL || *size == 0)
|
||||
return -EINVAL;
|
||||
|
||||
mypid = getpid();
|
||||
for (;;) {
|
||||
err = -ENOMEM;
|
||||
argsbuf_tmp = uv__realloc(argsbuf, argsbuf_size);
|
||||
if (argsbuf_tmp == NULL)
|
||||
goto out;
|
||||
argsbuf = argsbuf_tmp;
|
||||
mib[0] = CTL_KERN;
|
||||
mib[1] = KERN_PROC_ARGS;
|
||||
mib[2] = mypid;
|
||||
mib[3] = KERN_PROC_ARGV;
|
||||
if (sysctl(mib, 4, argsbuf, &argsbuf_size, NULL, 0) == 0) {
|
||||
break;
|
||||
}
|
||||
if (errno != ENOMEM) {
|
||||
err = -errno;
|
||||
goto out;
|
||||
}
|
||||
argsbuf_size *= 2U;
|
||||
}
|
||||
|
||||
if (argsbuf[0] == NULL) {
|
||||
err = -EINVAL; /* FIXME(bnoordhuis) More appropriate error. */
|
||||
goto out;
|
||||
}
|
||||
|
||||
*size -= 1;
|
||||
exepath_size = strlen(argsbuf[0]);
|
||||
if (*size > exepath_size)
|
||||
*size = exepath_size;
|
||||
|
||||
memcpy(buffer, argsbuf[0], *size);
|
||||
buffer[*size] = '\0';
|
||||
err = 0;
|
||||
|
||||
out:
|
||||
uv__free(argsbuf);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
uint64_t uv_get_free_memory(void) {
|
||||
struct uvmexp info;
|
||||
size_t size = sizeof(info);
|
||||
int which[] = {CTL_VM, VM_UVMEXP};
|
||||
|
||||
if (sysctl(which, 2, &info, &size, NULL, 0))
|
||||
return -errno;
|
||||
|
||||
return (uint64_t) info.free * sysconf(_SC_PAGESIZE);
|
||||
}
|
||||
|
||||
|
||||
uint64_t uv_get_total_memory(void) {
|
||||
uint64_t info;
|
||||
int which[] = {CTL_HW, HW_PHYSMEM64};
|
||||
size_t size = sizeof(info);
|
||||
|
||||
if (sysctl(which, 2, &info, &size, NULL, 0))
|
||||
return -errno;
|
||||
|
||||
return (uint64_t) info;
|
||||
}
|
||||
|
||||
|
||||
char** uv_setup_args(int argc, char** argv) {
|
||||
process_title = argc ? uv__strdup(argv[0]) : NULL;
|
||||
return argv;
|
||||
}
|
||||
|
||||
|
||||
int uv_set_process_title(const char* title) {
|
||||
uv__free(process_title);
|
||||
process_title = uv__strdup(title);
|
||||
setproctitle("%s", title);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv_get_process_title(char* buffer, size_t size) {
|
||||
size_t len;
|
||||
|
||||
if (buffer == NULL || size == 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (process_title) {
|
||||
len = strlen(process_title) + 1;
|
||||
|
||||
if (size < len)
|
||||
return -ENOBUFS;
|
||||
|
||||
memcpy(buffer, process_title, len);
|
||||
} else {
|
||||
len = 0;
|
||||
}
|
||||
|
||||
buffer[len] = '\0';
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv_resident_set_memory(size_t* rss) {
|
||||
struct kinfo_proc kinfo;
|
||||
size_t page_size = getpagesize();
|
||||
size_t size = sizeof(struct kinfo_proc);
|
||||
int mib[6];
|
||||
|
||||
mib[0] = CTL_KERN;
|
||||
mib[1] = KERN_PROC;
|
||||
mib[2] = KERN_PROC_PID;
|
||||
mib[3] = getpid();
|
||||
mib[4] = sizeof(struct kinfo_proc);
|
||||
mib[5] = 1;
|
||||
|
||||
if (sysctl(mib, 6, &kinfo, &size, NULL, 0) < 0)
|
||||
return -errno;
|
||||
|
||||
*rss = kinfo.p_vm_rssize * page_size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv_uptime(double* uptime) {
|
||||
time_t now;
|
||||
struct timeval info;
|
||||
size_t size = sizeof(info);
|
||||
static int which[] = {CTL_KERN, KERN_BOOTTIME};
|
||||
|
||||
if (sysctl(which, 2, &info, &size, NULL, 0))
|
||||
return -errno;
|
||||
|
||||
now = time(NULL);
|
||||
|
||||
*uptime = (double)(now - info.tv_sec);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) {
|
||||
unsigned int ticks = (unsigned int)sysconf(_SC_CLK_TCK),
|
||||
multiplier = ((uint64_t)1000L / ticks), cpuspeed;
|
||||
uint64_t info[CPUSTATES];
|
||||
char model[512];
|
||||
int numcpus = 1;
|
||||
int which[] = {CTL_HW,HW_MODEL,0};
|
||||
size_t size;
|
||||
int i;
|
||||
uv_cpu_info_t* cpu_info;
|
||||
|
||||
size = sizeof(model);
|
||||
if (sysctl(which, 2, &model, &size, NULL, 0))
|
||||
return -errno;
|
||||
|
||||
which[1] = HW_NCPU;
|
||||
size = sizeof(numcpus);
|
||||
if (sysctl(which, 2, &numcpus, &size, NULL, 0))
|
||||
return -errno;
|
||||
|
||||
*cpu_infos = uv__malloc(numcpus * sizeof(**cpu_infos));
|
||||
if (!(*cpu_infos))
|
||||
return -ENOMEM;
|
||||
|
||||
*count = numcpus;
|
||||
|
||||
which[1] = HW_CPUSPEED;
|
||||
size = sizeof(cpuspeed);
|
||||
if (sysctl(which, 2, &cpuspeed, &size, NULL, 0)) {
|
||||
uv__free(*cpu_infos);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
size = sizeof(info);
|
||||
which[0] = CTL_KERN;
|
||||
which[1] = KERN_CPTIME2;
|
||||
for (i = 0; i < numcpus; i++) {
|
||||
which[2] = i;
|
||||
size = sizeof(info);
|
||||
if (sysctl(which, 3, &info, &size, NULL, 0)) {
|
||||
uv__free(*cpu_infos);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
cpu_info = &(*cpu_infos)[i];
|
||||
|
||||
cpu_info->cpu_times.user = (uint64_t)(info[CP_USER]) * multiplier;
|
||||
cpu_info->cpu_times.nice = (uint64_t)(info[CP_NICE]) * multiplier;
|
||||
cpu_info->cpu_times.sys = (uint64_t)(info[CP_SYS]) * multiplier;
|
||||
cpu_info->cpu_times.idle = (uint64_t)(info[CP_IDLE]) * multiplier;
|
||||
cpu_info->cpu_times.irq = (uint64_t)(info[CP_INTR]) * multiplier;
|
||||
|
||||
cpu_info->model = uv__strdup(model);
|
||||
cpu_info->speed = cpuspeed;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
uv__free(cpu_infos[i].model);
|
||||
}
|
||||
|
||||
uv__free(cpu_infos);
|
||||
}
|
||||
@@ -0,0 +1,334 @@
|
||||
/* Copyright libuv project contributors. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
#include "os390-syscalls.h"
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <search.h>
|
||||
|
||||
#define CW_CONDVAR 32
|
||||
|
||||
#pragma linkage(BPX4CTW, OS)
|
||||
#pragma linkage(BPX1CTW, OS)
|
||||
|
||||
static int number_of_epolls;
|
||||
static QUEUE global_epoll_queue;
|
||||
static uv_mutex_t global_epoll_lock;
|
||||
static uv_once_t once = UV_ONCE_INIT;
|
||||
|
||||
int scandir(const char* maindir, struct dirent*** namelist,
|
||||
int (*filter)(const struct dirent*),
|
||||
int (*compar)(const struct dirent**,
|
||||
const struct dirent **)) {
|
||||
struct dirent** nl;
|
||||
struct dirent* dirent;
|
||||
unsigned count;
|
||||
size_t allocated;
|
||||
DIR* mdir;
|
||||
|
||||
nl = NULL;
|
||||
count = 0;
|
||||
allocated = 0;
|
||||
mdir = opendir(maindir);
|
||||
if (!mdir)
|
||||
return -1;
|
||||
|
||||
while (1) {
|
||||
dirent = readdir(mdir);
|
||||
if (!dirent)
|
||||
break;
|
||||
if (!filter || filter(dirent)) {
|
||||
struct dirent* copy;
|
||||
copy = uv__malloc(sizeof(*copy));
|
||||
if (!copy) {
|
||||
while (count) {
|
||||
dirent = nl[--count];
|
||||
uv__free(dirent);
|
||||
}
|
||||
uv__free(nl);
|
||||
closedir(mdir);
|
||||
errno = ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
memcpy(copy, dirent, sizeof(*copy));
|
||||
|
||||
nl = uv__realloc(nl, sizeof(*copy) * (count + 1));
|
||||
nl[count++] = copy;
|
||||
}
|
||||
}
|
||||
|
||||
qsort(nl, count, sizeof(struct dirent *),
|
||||
(int (*)(const void *, const void *)) compar);
|
||||
|
||||
closedir(mdir);
|
||||
|
||||
*namelist = nl;
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
static unsigned int next_power_of_two(unsigned int val) {
|
||||
val -= 1;
|
||||
val |= val >> 1;
|
||||
val |= val >> 2;
|
||||
val |= val >> 4;
|
||||
val |= val >> 8;
|
||||
val |= val >> 16;
|
||||
val += 1;
|
||||
return val;
|
||||
}
|
||||
|
||||
|
||||
static void maybe_resize(uv__os390_epoll* lst, unsigned int len) {
|
||||
unsigned int newsize;
|
||||
unsigned int i;
|
||||
struct pollfd* newlst;
|
||||
|
||||
if (len <= lst->size)
|
||||
return;
|
||||
|
||||
newsize = next_power_of_two(len);
|
||||
newlst = uv__realloc(lst->items, newsize * sizeof(lst->items[0]));
|
||||
|
||||
if (newlst == NULL)
|
||||
abort();
|
||||
for (i = lst->size; i < newsize; ++i)
|
||||
newlst[i].fd = -1;
|
||||
|
||||
lst->items = newlst;
|
||||
lst->size = newsize;
|
||||
}
|
||||
|
||||
|
||||
static void epoll_init(void) {
|
||||
QUEUE_INIT(&global_epoll_queue);
|
||||
if (uv_mutex_init(&global_epoll_lock))
|
||||
abort();
|
||||
}
|
||||
|
||||
|
||||
uv__os390_epoll* epoll_create1(int flags) {
|
||||
uv__os390_epoll* lst;
|
||||
|
||||
uv_once(&once, epoll_init);
|
||||
uv_mutex_lock(&global_epoll_lock);
|
||||
lst = uv__malloc(sizeof(*lst));
|
||||
if (lst == -1)
|
||||
return NULL;
|
||||
QUEUE_INSERT_TAIL(&global_epoll_queue, &lst->member);
|
||||
uv_mutex_unlock(&global_epoll_lock);
|
||||
|
||||
/* initialize list */
|
||||
lst->size = 0;
|
||||
lst->items = NULL;
|
||||
return lst;
|
||||
}
|
||||
|
||||
|
||||
int epoll_ctl(uv__os390_epoll* lst,
|
||||
int op,
|
||||
int fd,
|
||||
struct epoll_event *event) {
|
||||
if(op == EPOLL_CTL_DEL) {
|
||||
if (fd >= lst->size || lst->items[fd].fd == -1) {
|
||||
errno = ENOENT;
|
||||
return -1;
|
||||
}
|
||||
lst->items[fd].fd = -1;
|
||||
} else if(op == EPOLL_CTL_ADD) {
|
||||
maybe_resize(lst, fd + 1);
|
||||
if (lst->items[fd].fd != -1) {
|
||||
errno = EEXIST;
|
||||
return -1;
|
||||
}
|
||||
lst->items[fd].fd = fd;
|
||||
lst->items[fd].events = event->events;
|
||||
} else if(op == EPOLL_CTL_MOD) {
|
||||
if (fd >= lst->size || lst->items[fd].fd == -1) {
|
||||
errno = ENOENT;
|
||||
return -1;
|
||||
}
|
||||
lst->items[fd].events = event->events;
|
||||
} else
|
||||
abort();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int epoll_wait(uv__os390_epoll* lst, struct epoll_event* events,
|
||||
int maxevents, int timeout) {
|
||||
size_t size;
|
||||
struct pollfd* pfds;
|
||||
int pollret;
|
||||
int reventcount;
|
||||
|
||||
uv_mutex_lock(&global_epoll_lock);
|
||||
uv_mutex_unlock(&global_epoll_lock);
|
||||
size = lst->size;
|
||||
pfds = lst->items;
|
||||
pollret = poll(pfds, size, timeout);
|
||||
if(pollret == -1)
|
||||
return pollret;
|
||||
|
||||
reventcount = 0;
|
||||
for (int i = 0; i < lst->size && i < maxevents; ++i) {
|
||||
struct epoll_event ev;
|
||||
|
||||
ev.events = 0;
|
||||
ev.fd = pfds[i].fd;
|
||||
if(!pfds[i].revents)
|
||||
continue;
|
||||
|
||||
if(pfds[i].revents & POLLRDNORM)
|
||||
ev.events = ev.events | POLLIN;
|
||||
|
||||
if(pfds[i].revents & POLLWRNORM)
|
||||
ev.events = ev.events | POLLOUT;
|
||||
|
||||
if(pfds[i].revents & POLLHUP)
|
||||
ev.events = ev.events | POLLHUP;
|
||||
|
||||
pfds[i].revents = 0;
|
||||
events[reventcount++] = ev;
|
||||
}
|
||||
|
||||
return reventcount;
|
||||
}
|
||||
|
||||
|
||||
int epoll_file_close(int fd) {
|
||||
QUEUE* q;
|
||||
|
||||
uv_once(&once, epoll_init);
|
||||
uv_mutex_lock(&global_epoll_lock);
|
||||
QUEUE_FOREACH(q, &global_epoll_queue) {
|
||||
uv__os390_epoll* lst;
|
||||
|
||||
lst = QUEUE_DATA(q, uv__os390_epoll, member);
|
||||
if (fd < lst->size && lst->items != NULL && lst->items[fd].fd != -1)
|
||||
lst->items[fd].fd = -1;
|
||||
}
|
||||
|
||||
uv_mutex_unlock(&global_epoll_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void epoll_queue_close(uv__os390_epoll* lst) {
|
||||
uv_mutex_lock(&global_epoll_lock);
|
||||
QUEUE_REMOVE(&lst->member);
|
||||
uv_mutex_unlock(&global_epoll_lock);
|
||||
uv__free(lst->items);
|
||||
lst->items = NULL;
|
||||
}
|
||||
|
||||
|
||||
int nanosleep(const struct timespec* req, struct timespec* rem) {
|
||||
unsigned nano;
|
||||
unsigned seconds;
|
||||
unsigned events;
|
||||
unsigned secrem;
|
||||
unsigned nanorem;
|
||||
int rv;
|
||||
int rc;
|
||||
int rsn;
|
||||
|
||||
nano = (int)req->tv_nsec;
|
||||
seconds = req->tv_sec;
|
||||
events = CW_CONDVAR;
|
||||
|
||||
#if defined(_LP64)
|
||||
BPX4CTW(&seconds, &nano, &events, &secrem, &nanorem, &rv, &rc, &rsn);
|
||||
#else
|
||||
BPX1CTW(&seconds, &nano, &events, &secrem, &nanorem, &rv, &rc, &rsn);
|
||||
#endif
|
||||
|
||||
assert(rv == -1 && errno == EAGAIN);
|
||||
|
||||
if(rem != NULL) {
|
||||
rem->tv_nsec = nanorem;
|
||||
rem->tv_sec = secrem;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
char* mkdtemp(char* path) {
|
||||
static const char* tempchars =
|
||||
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
||||
static const size_t num_chars = 62;
|
||||
static const size_t num_x = 6;
|
||||
char *ep, *cp;
|
||||
unsigned int tries, i;
|
||||
size_t len;
|
||||
uint64_t v;
|
||||
int fd;
|
||||
int retval;
|
||||
int saved_errno;
|
||||
|
||||
len = strlen(path);
|
||||
ep = path + len;
|
||||
if (len < num_x || strncmp(ep - num_x, "XXXXXX", num_x)) {
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fd = open("/dev/urandom", O_RDONLY);
|
||||
if (fd == -1)
|
||||
return NULL;
|
||||
|
||||
tries = TMP_MAX;
|
||||
retval = -1;
|
||||
do {
|
||||
if (read(fd, &v, sizeof(v)) != sizeof(v))
|
||||
break;
|
||||
|
||||
cp = ep - num_x;
|
||||
for (i = 0; i < num_x; i++) {
|
||||
*cp++ = tempchars[v % num_chars];
|
||||
v /= num_chars;
|
||||
}
|
||||
|
||||
if (mkdir(path, S_IRWXU) == 0) {
|
||||
retval = 0;
|
||||
break;
|
||||
}
|
||||
else if (errno != EEXIST)
|
||||
break;
|
||||
} while (--tries);
|
||||
|
||||
saved_errno = errno;
|
||||
uv__close(fd);
|
||||
if (tries == 0) {
|
||||
errno = EEXIST;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (retval == -1) {
|
||||
errno = saved_errno;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/* Copyright libuv project contributors. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef UV_OS390_SYSCALL_H_
|
||||
#define UV_OS390_SYSCALL_H_
|
||||
|
||||
#include "uv.h"
|
||||
#include "internal.h"
|
||||
#include <dirent.h>
|
||||
#include <poll.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#define EPOLL_CTL_ADD 1
|
||||
#define EPOLL_CTL_DEL 2
|
||||
#define EPOLL_CTL_MOD 3
|
||||
#define MAX_EPOLL_INSTANCES 256
|
||||
#define MAX_ITEMS_PER_EPOLL 1024
|
||||
|
||||
#define UV__O_CLOEXEC 0x80000
|
||||
#define UV__EPOLL_CLOEXEC UV__O_CLOEXEC
|
||||
#define UV__EPOLL_CTL_ADD EPOLL_CTL_ADD
|
||||
#define UV__EPOLL_CTL_DEL EPOLL_CTL_DEL
|
||||
#define UV__EPOLL_CTL_MOD EPOLL_CTL_MOD
|
||||
|
||||
struct epoll_event {
|
||||
int events;
|
||||
int fd;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
QUEUE member;
|
||||
struct pollfd* items;
|
||||
unsigned long size;
|
||||
} uv__os390_epoll;
|
||||
|
||||
/* epoll api */
|
||||
uv__os390_epoll* epoll_create1(int flags);
|
||||
int epoll_ctl(uv__os390_epoll* ep, int op, int fd, struct epoll_event *event);
|
||||
int epoll_wait(uv__os390_epoll* ep, struct epoll_event *events, int maxevents, int timeout);
|
||||
int epoll_file_close(int fd);
|
||||
|
||||
/* utility functions */
|
||||
int nanosleep(const struct timespec* req, struct timespec* rem);
|
||||
int scandir(const char* maindir, struct dirent*** namelist,
|
||||
int (*filter)(const struct dirent *),
|
||||
int (*compar)(const struct dirent **,
|
||||
const struct dirent **));
|
||||
char *mkdtemp(char* path);
|
||||
|
||||
#endif /* UV_OS390_SYSCALL_H_ */
|
||||
@@ -0,0 +1,849 @@
|
||||
/* Copyright libuv project contributors. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "internal.h"
|
||||
#include <sys/ioctl.h>
|
||||
#include <net/if.h>
|
||||
#include <utmpx.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/ps.h>
|
||||
#if defined(__clang__)
|
||||
#include "csrsic.h"
|
||||
#else
|
||||
#include "//'SYS1.SAMPLIB(CSRSIC)'"
|
||||
#endif
|
||||
|
||||
#define CVT_PTR 0x10
|
||||
#define CSD_OFFSET 0x294
|
||||
|
||||
/*
|
||||
Long-term average CPU service used by this logical partition,
|
||||
in millions of service units per hour. If this value is above
|
||||
the partition's defined capacity, the partition will be capped.
|
||||
It is calculated using the physical CPU adjustment factor
|
||||
(RCTPCPUA) so it may not match other measures of service which
|
||||
are based on the logical CPU adjustment factor. It is available
|
||||
if the hardware supports LPAR cluster.
|
||||
*/
|
||||
#define RCTLACS_OFFSET 0xC4
|
||||
|
||||
/* 32-bit count of alive CPUs. This includes both CPs and IFAs */
|
||||
#define CSD_NUMBER_ONLINE_CPUS 0xD4
|
||||
|
||||
/* Address of system resources manager (SRM) control table */
|
||||
#define CVTOPCTP_OFFSET 0x25C
|
||||
|
||||
/* Address of the RCT table */
|
||||
#define RMCTRCT_OFFSET 0xE4
|
||||
|
||||
/* Address of the rsm control and enumeration area. */
|
||||
#define CVTRCEP_OFFSET 0x490
|
||||
|
||||
/*
|
||||
Number of frames currently available to system.
|
||||
Excluded are frames backing perm storage, frames offline, and bad frames.
|
||||
*/
|
||||
#define RCEPOOL_OFFSET 0x004
|
||||
|
||||
/* Total number of frames currently on all available frame queues. */
|
||||
#define RCEAFC_OFFSET 0x088
|
||||
|
||||
/* CPC model length from the CSRSI Service. */
|
||||
#define CPCMODEL_LENGTH 16
|
||||
|
||||
/* Thread Entry constants */
|
||||
#define PGTH_CURRENT 1
|
||||
#define PGTH_LEN 26
|
||||
#define PGTHAPATH 0x20
|
||||
#pragma linkage(BPX4GTH, OS)
|
||||
#pragma linkage(BPX1GTH, OS)
|
||||
|
||||
typedef unsigned data_area_ptr_assign_type;
|
||||
|
||||
typedef union {
|
||||
struct {
|
||||
#if defined(_LP64)
|
||||
data_area_ptr_assign_type lower;
|
||||
#endif
|
||||
data_area_ptr_assign_type assign;
|
||||
};
|
||||
char* deref;
|
||||
} data_area_ptr;
|
||||
|
||||
|
||||
void uv_loadavg(double avg[3]) {
|
||||
/* TODO: implement the following */
|
||||
avg[0] = 0;
|
||||
avg[1] = 0;
|
||||
avg[2] = 0;
|
||||
}
|
||||
|
||||
|
||||
int uv__platform_loop_init(uv_loop_t* loop) {
|
||||
uv__os390_epoll* ep;
|
||||
|
||||
ep = epoll_create1(UV__EPOLL_CLOEXEC);
|
||||
loop->ep = ep;
|
||||
if (ep == NULL)
|
||||
return -errno;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void uv__platform_loop_delete(uv_loop_t* loop) {
|
||||
if (loop->ep != NULL) {
|
||||
epoll_queue_close(loop->ep);
|
||||
loop->ep = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
uint64_t uv__hrtime(uv_clocktype_t type) {
|
||||
struct timeval time;
|
||||
gettimeofday(&time, NULL);
|
||||
return (uint64_t) time.tv_sec * 1e9 + time.tv_usec * 1e3;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Get the exe path using the thread entry information
|
||||
in the address space.
|
||||
*/
|
||||
static int getexe(const int pid, char* buf, size_t len) {
|
||||
struct {
|
||||
int pid;
|
||||
int thid[2];
|
||||
char accesspid;
|
||||
char accessthid;
|
||||
char asid[2];
|
||||
char loginname[8];
|
||||
char flag;
|
||||
char len;
|
||||
} Input_data;
|
||||
|
||||
union {
|
||||
struct {
|
||||
char gthb[4];
|
||||
int pid;
|
||||
int thid[2];
|
||||
char accesspid;
|
||||
char accessthid[3];
|
||||
int lenused;
|
||||
int offsetProcess;
|
||||
int offsetConTTY;
|
||||
int offsetPath;
|
||||
int offsetCommand;
|
||||
int offsetFileData;
|
||||
int offsetThread;
|
||||
} Output_data;
|
||||
char buf[2048];
|
||||
} Output_buf;
|
||||
|
||||
struct Output_path_type {
|
||||
char gthe[4];
|
||||
short int len;
|
||||
char path[1024];
|
||||
};
|
||||
|
||||
int Input_length;
|
||||
int Output_length;
|
||||
void* Input_address;
|
||||
void* Output_address;
|
||||
struct Output_path_type* Output_path;
|
||||
int rv;
|
||||
int rc;
|
||||
int rsn;
|
||||
|
||||
Input_length = PGTH_LEN;
|
||||
Output_length = sizeof(Output_buf);
|
||||
Output_address = &Output_buf;
|
||||
Input_address = &Input_data;
|
||||
memset(&Input_data, 0, sizeof Input_data);
|
||||
Input_data.flag |= PGTHAPATH;
|
||||
Input_data.pid = pid;
|
||||
Input_data.accesspid = PGTH_CURRENT;
|
||||
|
||||
#ifdef _LP64
|
||||
BPX4GTH(&Input_length,
|
||||
&Input_address,
|
||||
&Output_length,
|
||||
&Output_address,
|
||||
&rv,
|
||||
&rc,
|
||||
&rsn);
|
||||
#else
|
||||
BPX1GTH(&Input_length,
|
||||
&Input_address,
|
||||
&Output_length,
|
||||
&Output_address,
|
||||
&rv,
|
||||
&rc,
|
||||
&rsn);
|
||||
#endif
|
||||
|
||||
if (rv == -1) {
|
||||
errno = rc;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Check highest byte to ensure data availability */
|
||||
assert(((Output_buf.Output_data.offsetPath >>24) & 0xFF) == 'A');
|
||||
|
||||
/* Get the offset from the lowest 3 bytes */
|
||||
Output_path = (char*)(&Output_buf) +
|
||||
(Output_buf.Output_data.offsetPath & 0x00FFFFFF);
|
||||
|
||||
if (Output_path->len >= len) {
|
||||
errno = ENOBUFS;
|
||||
return -1;
|
||||
}
|
||||
|
||||
strncpy(buf, Output_path->path, len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* We could use a static buffer for the path manipulations that we need outside
|
||||
* of the function, but this function could be called by multiple consumers and
|
||||
* we don't want to potentially create a race condition in the use of snprintf.
|
||||
* There is no direct way of getting the exe path in zOS - either through /procfs
|
||||
* or through some libc APIs. The below approach is to parse the argv[0]'s pattern
|
||||
* and use it in conjunction with PATH environment variable to craft one.
|
||||
*/
|
||||
int uv_exepath(char* buffer, size_t* size) {
|
||||
int res;
|
||||
char args[PATH_MAX];
|
||||
char abspath[PATH_MAX];
|
||||
size_t abspath_size;
|
||||
int pid;
|
||||
|
||||
if (buffer == NULL || size == NULL || *size == 0)
|
||||
return -EINVAL;
|
||||
|
||||
pid = getpid();
|
||||
res = getexe(pid, args, sizeof(args));
|
||||
if (res < 0)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Possibilities for args:
|
||||
* i) an absolute path such as: /home/user/myprojects/nodejs/node
|
||||
* ii) a relative path such as: ./node or ../myprojects/nodejs/node
|
||||
* iii) a bare filename such as "node", after exporting PATH variable
|
||||
* to its location.
|
||||
*/
|
||||
|
||||
/* Case i) and ii) absolute or relative paths */
|
||||
if (strchr(args, '/') != NULL) {
|
||||
if (realpath(args, abspath) != abspath)
|
||||
return -errno;
|
||||
|
||||
abspath_size = strlen(abspath);
|
||||
|
||||
*size -= 1;
|
||||
if (*size > abspath_size)
|
||||
*size = abspath_size;
|
||||
|
||||
memcpy(buffer, abspath, *size);
|
||||
buffer[*size] = '\0';
|
||||
|
||||
return 0;
|
||||
} else {
|
||||
/* Case iii). Search PATH environment variable */
|
||||
char trypath[PATH_MAX];
|
||||
char* clonedpath = NULL;
|
||||
char* token = NULL;
|
||||
char* path = getenv("PATH");
|
||||
|
||||
if (path == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
clonedpath = uv__strdup(path);
|
||||
if (clonedpath == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
token = strtok(clonedpath, ":");
|
||||
while (token != NULL) {
|
||||
snprintf(trypath, sizeof(trypath) - 1, "%s/%s", token, args);
|
||||
if (realpath(trypath, abspath) == abspath) {
|
||||
/* Check the match is executable */
|
||||
if (access(abspath, X_OK) == 0) {
|
||||
abspath_size = strlen(abspath);
|
||||
|
||||
*size -= 1;
|
||||
if (*size > abspath_size)
|
||||
*size = abspath_size;
|
||||
|
||||
memcpy(buffer, abspath, *size);
|
||||
buffer[*size] = '\0';
|
||||
|
||||
uv__free(clonedpath);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
token = strtok(NULL, ":");
|
||||
}
|
||||
uv__free(clonedpath);
|
||||
|
||||
/* Out of tokens (path entries), and no match found */
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
uint64_t uv_get_free_memory(void) {
|
||||
uint64_t freeram;
|
||||
|
||||
data_area_ptr cvt = {0};
|
||||
data_area_ptr rcep = {0};
|
||||
cvt.assign = *(data_area_ptr_assign_type*)(CVT_PTR);
|
||||
rcep.assign = *(data_area_ptr_assign_type*)(cvt.deref + CVTRCEP_OFFSET);
|
||||
freeram = *((uint64_t*)(rcep.deref + RCEAFC_OFFSET)) * 4;
|
||||
return freeram;
|
||||
}
|
||||
|
||||
|
||||
uint64_t uv_get_total_memory(void) {
|
||||
uint64_t totalram;
|
||||
|
||||
data_area_ptr cvt = {0};
|
||||
data_area_ptr rcep = {0};
|
||||
cvt.assign = *(data_area_ptr_assign_type*)(CVT_PTR);
|
||||
rcep.assign = *(data_area_ptr_assign_type*)(cvt.deref + CVTRCEP_OFFSET);
|
||||
totalram = *((uint64_t*)(rcep.deref + RCEPOOL_OFFSET)) * 4;
|
||||
return totalram;
|
||||
}
|
||||
|
||||
|
||||
int uv_resident_set_memory(size_t* rss) {
|
||||
W_PSPROC buf;
|
||||
|
||||
memset(&buf, 0, sizeof(buf));
|
||||
if (w_getpsent(0, &buf, sizeof(W_PSPROC)) == -1)
|
||||
return -EINVAL;
|
||||
|
||||
*rss = buf.ps_size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv_uptime(double* uptime) {
|
||||
struct utmpx u ;
|
||||
struct utmpx *v;
|
||||
time64_t t;
|
||||
|
||||
u.ut_type = BOOT_TIME;
|
||||
v = getutxid(&u);
|
||||
if (v == NULL)
|
||||
return -1;
|
||||
*uptime = difftime64(time64(&t), v->ut_tv.tv_sec);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) {
|
||||
uv_cpu_info_t* cpu_info;
|
||||
int result;
|
||||
int idx;
|
||||
siv1v2 info;
|
||||
data_area_ptr cvt = {0};
|
||||
data_area_ptr csd = {0};
|
||||
data_area_ptr rmctrct = {0};
|
||||
data_area_ptr cvtopctp = {0};
|
||||
int cpu_usage_avg;
|
||||
|
||||
cvt.assign = *(data_area_ptr_assign_type*)(CVT_PTR);
|
||||
|
||||
csd.assign = *((data_area_ptr_assign_type *) (cvt.deref + CSD_OFFSET));
|
||||
cvtopctp.assign = *((data_area_ptr_assign_type *) (cvt.deref + CVTOPCTP_OFFSET));
|
||||
rmctrct.assign = *((data_area_ptr_assign_type *) (cvtopctp.deref + RMCTRCT_OFFSET));
|
||||
|
||||
*count = *((int*) (csd.deref + CSD_NUMBER_ONLINE_CPUS));
|
||||
cpu_usage_avg = *((unsigned short int*) (rmctrct.deref + RCTLACS_OFFSET));
|
||||
|
||||
*cpu_infos = uv__malloc(*count * sizeof(uv_cpu_info_t));
|
||||
if (!*cpu_infos)
|
||||
return -ENOMEM;
|
||||
|
||||
cpu_info = *cpu_infos;
|
||||
idx = 0;
|
||||
while (idx < *count) {
|
||||
cpu_info->speed = *(int*)(info.siv1v2si22v1.si22v1cpucapability);
|
||||
cpu_info->model = uv__malloc(CPCMODEL_LENGTH + 1);
|
||||
memset(cpu_info->model, '\0', CPCMODEL_LENGTH + 1);
|
||||
memcpy(cpu_info->model, info.siv1v2si11v1.si11v1cpcmodel, CPCMODEL_LENGTH);
|
||||
cpu_info->cpu_times.user = cpu_usage_avg;
|
||||
/* TODO: implement the following */
|
||||
cpu_info->cpu_times.sys = 0;
|
||||
cpu_info->cpu_times.idle = 0;
|
||||
cpu_info->cpu_times.irq = 0;
|
||||
cpu_info->cpu_times.nice = 0;
|
||||
++cpu_info;
|
||||
++idx;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count) {
|
||||
for (int i = 0; i < count; ++i)
|
||||
uv__free(cpu_infos[i].model);
|
||||
uv__free(cpu_infos);
|
||||
}
|
||||
|
||||
|
||||
static int uv__interface_addresses_v6(uv_interface_address_t** addresses,
|
||||
int* count) {
|
||||
uv_interface_address_t* address;
|
||||
int sockfd;
|
||||
int maxsize;
|
||||
__net_ifconf6header_t ifc;
|
||||
__net_ifconf6entry_t* ifr;
|
||||
__net_ifconf6entry_t* p;
|
||||
__net_ifconf6entry_t flg;
|
||||
|
||||
*count = 0;
|
||||
/* Assume maximum buffer size allowable */
|
||||
maxsize = 16384;
|
||||
|
||||
if (0 > (sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP)))
|
||||
return -errno;
|
||||
|
||||
ifc.__nif6h_version = 1;
|
||||
ifc.__nif6h_buflen = maxsize;
|
||||
ifc.__nif6h_buffer = uv__calloc(1, maxsize);;
|
||||
|
||||
if (ioctl(sockfd, SIOCGIFCONF6, &ifc) == -1) {
|
||||
uv__close(sockfd);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
|
||||
*count = 0;
|
||||
ifr = (__net_ifconf6entry_t*)(ifc.__nif6h_buffer);
|
||||
while ((char*)ifr < (char*)ifc.__nif6h_buffer + ifc.__nif6h_buflen) {
|
||||
p = ifr;
|
||||
ifr = (__net_ifconf6entry_t*)((char*)ifr + ifc.__nif6h_entrylen);
|
||||
|
||||
if (!(p->__nif6e_addr.sin6_family == AF_INET6 ||
|
||||
p->__nif6e_addr.sin6_family == AF_INET))
|
||||
continue;
|
||||
|
||||
if (!(p->__nif6e_flags & _NIF6E_FLAGS_ON_LINK_ACTIVE))
|
||||
continue;
|
||||
|
||||
++(*count);
|
||||
}
|
||||
|
||||
/* Alloc the return interface structs */
|
||||
*addresses = uv__malloc(*count * sizeof(uv_interface_address_t));
|
||||
if (!(*addresses)) {
|
||||
uv__close(sockfd);
|
||||
return -ENOMEM;
|
||||
}
|
||||
address = *addresses;
|
||||
|
||||
ifr = (__net_ifconf6entry_t*)(ifc.__nif6h_buffer);
|
||||
while ((char*)ifr < (char*)ifc.__nif6h_buffer + ifc.__nif6h_buflen) {
|
||||
p = ifr;
|
||||
ifr = (__net_ifconf6entry_t*)((char*)ifr + ifc.__nif6h_entrylen);
|
||||
|
||||
if (!(p->__nif6e_addr.sin6_family == AF_INET6 ||
|
||||
p->__nif6e_addr.sin6_family == AF_INET))
|
||||
continue;
|
||||
|
||||
if (!(p->__nif6e_flags & _NIF6E_FLAGS_ON_LINK_ACTIVE))
|
||||
continue;
|
||||
|
||||
/* All conditions above must match count loop */
|
||||
|
||||
address->name = uv__strdup(p->__nif6e_name);
|
||||
|
||||
if (p->__nif6e_addr.sin6_family == AF_INET6)
|
||||
address->address.address6 = *((struct sockaddr_in6*) &p->__nif6e_addr);
|
||||
else
|
||||
address->address.address4 = *((struct sockaddr_in*) &p->__nif6e_addr);
|
||||
|
||||
/* TODO: Retrieve netmask using SIOCGIFNETMASK ioctl */
|
||||
|
||||
address->is_internal = flg.__nif6e_flags & _NIF6E_FLAGS_LOOPBACK ? 1 : 0;
|
||||
|
||||
address++;
|
||||
}
|
||||
|
||||
uv__close(sockfd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
|
||||
uv_interface_address_t* address;
|
||||
int sockfd;
|
||||
int maxsize;
|
||||
struct ifconf ifc;
|
||||
struct ifreq flg;
|
||||
struct ifreq* ifr;
|
||||
struct ifreq* p;
|
||||
int count_v6;
|
||||
|
||||
/* get the ipv6 addresses first */
|
||||
uv_interface_address_t* addresses_v6;
|
||||
uv__interface_addresses_v6(&addresses_v6, &count_v6);
|
||||
|
||||
/* now get the ipv4 addresses */
|
||||
*count = 0;
|
||||
|
||||
/* Assume maximum buffer size allowable */
|
||||
maxsize = 16384;
|
||||
|
||||
sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
|
||||
if (0 > sockfd)
|
||||
return -errno;
|
||||
|
||||
ifc.ifc_req = uv__calloc(1, maxsize);
|
||||
ifc.ifc_len = maxsize;
|
||||
if (ioctl(sockfd, SIOCGIFCONF, &ifc) == -1) {
|
||||
uv__close(sockfd);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
#define MAX(a,b) (((a)>(b))?(a):(b))
|
||||
#define ADDR_SIZE(p) MAX((p).sa_len, sizeof(p))
|
||||
|
||||
/* Count all up and running ipv4/ipv6 addresses */
|
||||
ifr = ifc.ifc_req;
|
||||
while ((char*)ifr < (char*)ifc.ifc_req + ifc.ifc_len) {
|
||||
p = ifr;
|
||||
ifr = (struct ifreq*)
|
||||
((char*)ifr + sizeof(ifr->ifr_name) + ADDR_SIZE(ifr->ifr_addr));
|
||||
|
||||
if (!(p->ifr_addr.sa_family == AF_INET6 ||
|
||||
p->ifr_addr.sa_family == AF_INET))
|
||||
continue;
|
||||
|
||||
memcpy(flg.ifr_name, p->ifr_name, sizeof(flg.ifr_name));
|
||||
if (ioctl(sockfd, SIOCGIFFLAGS, &flg) == -1) {
|
||||
uv__close(sockfd);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
if (!(flg.ifr_flags & IFF_UP && flg.ifr_flags & IFF_RUNNING))
|
||||
continue;
|
||||
|
||||
(*count)++;
|
||||
}
|
||||
|
||||
/* Alloc the return interface structs */
|
||||
*addresses = uv__malloc((*count + count_v6) *
|
||||
sizeof(uv_interface_address_t));
|
||||
|
||||
if (!(*addresses)) {
|
||||
uv__close(sockfd);
|
||||
return -ENOMEM;
|
||||
}
|
||||
address = *addresses;
|
||||
|
||||
/* copy over the ipv6 addresses */
|
||||
memcpy(address, addresses_v6, count_v6 * sizeof(uv_interface_address_t));
|
||||
address += count_v6;
|
||||
*count += count_v6;
|
||||
uv__free(addresses_v6);
|
||||
|
||||
ifr = ifc.ifc_req;
|
||||
while ((char*)ifr < (char*)ifc.ifc_req + ifc.ifc_len) {
|
||||
p = ifr;
|
||||
ifr = (struct ifreq*)
|
||||
((char*)ifr + sizeof(ifr->ifr_name) + ADDR_SIZE(ifr->ifr_addr));
|
||||
|
||||
if (!(p->ifr_addr.sa_family == AF_INET6 ||
|
||||
p->ifr_addr.sa_family == AF_INET))
|
||||
continue;
|
||||
|
||||
memcpy(flg.ifr_name, p->ifr_name, sizeof(flg.ifr_name));
|
||||
if (ioctl(sockfd, SIOCGIFFLAGS, &flg) == -1) {
|
||||
uv__close(sockfd);
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
if (!(flg.ifr_flags & IFF_UP && flg.ifr_flags & IFF_RUNNING))
|
||||
continue;
|
||||
|
||||
/* All conditions above must match count loop */
|
||||
|
||||
address->name = uv__strdup(p->ifr_name);
|
||||
|
||||
if (p->ifr_addr.sa_family == AF_INET6) {
|
||||
address->address.address6 = *((struct sockaddr_in6*) &p->ifr_addr);
|
||||
} else {
|
||||
address->address.address4 = *((struct sockaddr_in*) &p->ifr_addr);
|
||||
}
|
||||
|
||||
address->is_internal = flg.ifr_flags & IFF_LOOPBACK ? 1 : 0;
|
||||
address++;
|
||||
}
|
||||
|
||||
#undef ADDR_SIZE
|
||||
#undef MAX
|
||||
|
||||
uv__close(sockfd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void uv_free_interface_addresses(uv_interface_address_t* addresses,
|
||||
int count) {
|
||||
int i;
|
||||
for (i = 0; i < count; ++i)
|
||||
uv__free(addresses[i].name);
|
||||
uv__free(addresses);
|
||||
}
|
||||
|
||||
|
||||
void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) {
|
||||
struct epoll_event* events;
|
||||
struct epoll_event dummy;
|
||||
uintptr_t i;
|
||||
uintptr_t nfds;
|
||||
|
||||
assert(loop->watchers != NULL);
|
||||
|
||||
events = (struct epoll_event*) loop->watchers[loop->nwatchers];
|
||||
nfds = (uintptr_t) loop->watchers[loop->nwatchers + 1];
|
||||
if (events != NULL)
|
||||
/* Invalidate events with same file descriptor */
|
||||
for (i = 0; i < nfds; i++)
|
||||
if ((int) events[i].fd == fd)
|
||||
events[i].fd = -1;
|
||||
|
||||
/* Remove the file descriptor from the epoll. */
|
||||
if (loop->ep != NULL)
|
||||
epoll_ctl(loop->ep, UV__EPOLL_CTL_DEL, fd, &dummy);
|
||||
}
|
||||
|
||||
|
||||
int uv__io_check_fd(uv_loop_t* loop, int fd) {
|
||||
struct pollfd p[1];
|
||||
int rv;
|
||||
|
||||
p[0].fd = fd;
|
||||
p[0].events = POLLIN;
|
||||
|
||||
do
|
||||
rv = poll(p, 1, 0);
|
||||
while (rv == -1 && errno == EINTR);
|
||||
|
||||
if (rv == -1)
|
||||
abort();
|
||||
|
||||
if (p[0].revents & POLLNVAL)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void uv__io_poll(uv_loop_t* loop, int timeout) {
|
||||
static const int max_safe_timeout = 1789569;
|
||||
struct epoll_event events[1024];
|
||||
struct epoll_event* pe;
|
||||
struct epoll_event e;
|
||||
int real_timeout;
|
||||
QUEUE* q;
|
||||
uv__io_t* w;
|
||||
uint64_t base;
|
||||
int count;
|
||||
int nfds;
|
||||
int fd;
|
||||
int op;
|
||||
int i;
|
||||
|
||||
if (loop->nfds == 0) {
|
||||
assert(QUEUE_EMPTY(&loop->watcher_queue));
|
||||
return;
|
||||
}
|
||||
|
||||
while (!QUEUE_EMPTY(&loop->watcher_queue)) {
|
||||
uv_stream_t* stream;
|
||||
|
||||
q = QUEUE_HEAD(&loop->watcher_queue);
|
||||
QUEUE_REMOVE(q);
|
||||
QUEUE_INIT(q);
|
||||
w = QUEUE_DATA(q, uv__io_t, watcher_queue);
|
||||
|
||||
assert(w->pevents != 0);
|
||||
assert(w->fd >= 0);
|
||||
|
||||
stream= container_of(w, uv_stream_t, io_watcher);
|
||||
|
||||
assert(w->fd < (int) loop->nwatchers);
|
||||
|
||||
e.events = w->pevents;
|
||||
e.fd = w->fd;
|
||||
|
||||
if (w->events == 0)
|
||||
op = UV__EPOLL_CTL_ADD;
|
||||
else
|
||||
op = UV__EPOLL_CTL_MOD;
|
||||
|
||||
/* XXX Future optimization: do EPOLL_CTL_MOD lazily if we stop watching
|
||||
* events, skip the syscall and squelch the events after epoll_wait().
|
||||
*/
|
||||
if (epoll_ctl(loop->ep, op, w->fd, &e)) {
|
||||
if (errno != EEXIST)
|
||||
abort();
|
||||
|
||||
assert(op == UV__EPOLL_CTL_ADD);
|
||||
|
||||
/* We've reactivated a file descriptor that's been watched before. */
|
||||
if (epoll_ctl(loop->ep, UV__EPOLL_CTL_MOD, w->fd, &e))
|
||||
abort();
|
||||
}
|
||||
|
||||
w->events = w->pevents;
|
||||
}
|
||||
|
||||
assert(timeout >= -1);
|
||||
base = loop->time;
|
||||
count = 48; /* Benchmarks suggest this gives the best throughput. */
|
||||
real_timeout = timeout;
|
||||
int nevents = 0;
|
||||
|
||||
nfds = 0;
|
||||
for (;;) {
|
||||
if (sizeof(int32_t) == sizeof(long) && timeout >= max_safe_timeout)
|
||||
timeout = max_safe_timeout;
|
||||
|
||||
nfds = epoll_wait(loop->ep, events,
|
||||
ARRAY_SIZE(events), timeout);
|
||||
|
||||
/* Update loop->time unconditionally. It's tempting to skip the update when
|
||||
* timeout == 0 (i.e. non-blocking poll) but there is no guarantee that the
|
||||
* operating system didn't reschedule our process while in the syscall.
|
||||
*/
|
||||
base = loop->time;
|
||||
SAVE_ERRNO(uv__update_time(loop));
|
||||
if (nfds == 0) {
|
||||
assert(timeout != -1);
|
||||
timeout = real_timeout - timeout;
|
||||
if (timeout > 0)
|
||||
continue;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (nfds == -1) {
|
||||
|
||||
if (errno != EINTR)
|
||||
abort();
|
||||
|
||||
if (timeout == -1)
|
||||
continue;
|
||||
|
||||
if (timeout == 0)
|
||||
return;
|
||||
|
||||
/* Interrupted by a signal. Update timeout and poll again. */
|
||||
goto update_timeout;
|
||||
}
|
||||
|
||||
|
||||
assert(loop->watchers != NULL);
|
||||
loop->watchers[loop->nwatchers] = (void*) events;
|
||||
loop->watchers[loop->nwatchers + 1] = (void*) (uintptr_t) nfds;
|
||||
for (i = 0; i < nfds; i++) {
|
||||
pe = events + i;
|
||||
fd = pe->fd;
|
||||
|
||||
/* Skip invalidated events, see uv__platform_invalidate_fd */
|
||||
if (fd == -1)
|
||||
continue;
|
||||
|
||||
assert(fd >= 0);
|
||||
assert((unsigned) fd < loop->nwatchers);
|
||||
|
||||
w = loop->watchers[fd];
|
||||
|
||||
if (w == NULL) {
|
||||
/* File descriptor that we've stopped watching, disarm it.
|
||||
*
|
||||
* Ignore all errors because we may be racing with another thread
|
||||
* when the file descriptor is closed.
|
||||
*/
|
||||
epoll_ctl(loop->ep, UV__EPOLL_CTL_DEL, fd, pe);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Give users only events they're interested in. Prevents spurious
|
||||
* callbacks when previous callback invocation in this loop has stopped
|
||||
* the current watcher. Also, filters out events that users has not
|
||||
* requested us to watch.
|
||||
*/
|
||||
pe->events &= w->pevents | POLLERR | POLLHUP;
|
||||
|
||||
if (pe->events == POLLERR || pe->events == POLLHUP)
|
||||
pe->events |= w->pevents & (POLLIN | POLLOUT);
|
||||
|
||||
if (pe->events != 0) {
|
||||
w->cb(loop, w, pe->events);
|
||||
nevents++;
|
||||
}
|
||||
}
|
||||
loop->watchers[loop->nwatchers] = NULL;
|
||||
loop->watchers[loop->nwatchers + 1] = NULL;
|
||||
|
||||
if (nevents != 0) {
|
||||
if (nfds == ARRAY_SIZE(events) && --count != 0) {
|
||||
/* Poll for more events but don't block this time. */
|
||||
timeout = 0;
|
||||
continue;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (timeout == 0)
|
||||
return;
|
||||
|
||||
if (timeout == -1)
|
||||
continue;
|
||||
|
||||
update_timeout:
|
||||
assert(timeout > 0);
|
||||
|
||||
real_timeout -= (loop->time - base);
|
||||
if (real_timeout <= 0)
|
||||
return;
|
||||
|
||||
timeout = real_timeout;
|
||||
}
|
||||
}
|
||||
|
||||
void uv__set_process_title(const char* title) {
|
||||
/* do nothing */
|
||||
}
|
||||
|
||||
int uv__io_fork(uv_loop_t* loop) {
|
||||
uv__platform_loop_delete(loop);
|
||||
|
||||
return uv__platform_loop_init(loop);
|
||||
}
|
||||
@@ -0,0 +1,304 @@
|
||||
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "uv.h"
|
||||
#include "internal.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <sys/un.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
|
||||
int uv_pipe_init(uv_loop_t* loop, uv_pipe_t* handle, int ipc) {
|
||||
uv__stream_init(loop, (uv_stream_t*)handle, UV_NAMED_PIPE);
|
||||
handle->shutdown_req = NULL;
|
||||
handle->connect_req = NULL;
|
||||
handle->pipe_fname = NULL;
|
||||
handle->ipc = ipc;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv_pipe_bind(uv_pipe_t* handle, const char* name) {
|
||||
struct sockaddr_un saddr;
|
||||
const char* pipe_fname;
|
||||
int sockfd;
|
||||
int err;
|
||||
|
||||
pipe_fname = NULL;
|
||||
|
||||
/* Already bound? */
|
||||
if (uv__stream_fd(handle) >= 0)
|
||||
return -EINVAL;
|
||||
|
||||
/* Make a copy of the file name, it outlives this function's scope. */
|
||||
pipe_fname = uv__strdup(name);
|
||||
if (pipe_fname == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
/* We've got a copy, don't touch the original any more. */
|
||||
name = NULL;
|
||||
|
||||
err = uv__socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (err < 0)
|
||||
goto err_socket;
|
||||
sockfd = err;
|
||||
|
||||
memset(&saddr, 0, sizeof saddr);
|
||||
strncpy(saddr.sun_path, pipe_fname, sizeof(saddr.sun_path) - 1);
|
||||
saddr.sun_path[sizeof(saddr.sun_path) - 1] = '\0';
|
||||
saddr.sun_family = AF_UNIX;
|
||||
|
||||
if (bind(sockfd, (struct sockaddr*)&saddr, sizeof saddr)) {
|
||||
err = -errno;
|
||||
/* Convert ENOENT to EACCES for compatibility with Windows. */
|
||||
if (err == -ENOENT)
|
||||
err = -EACCES;
|
||||
|
||||
uv__close(sockfd);
|
||||
goto err_socket;
|
||||
}
|
||||
|
||||
/* Success. */
|
||||
handle->flags |= UV_HANDLE_BOUND;
|
||||
handle->pipe_fname = pipe_fname; /* Is a strdup'ed copy. */
|
||||
handle->io_watcher.fd = sockfd;
|
||||
return 0;
|
||||
|
||||
err_socket:
|
||||
uv__free((void*)pipe_fname);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
int uv_pipe_listen(uv_pipe_t* handle, int backlog, uv_connection_cb cb) {
|
||||
if (uv__stream_fd(handle) == -1)
|
||||
return -EINVAL;
|
||||
|
||||
#if defined(__MVS__)
|
||||
/* On zOS, backlog=0 has undefined behaviour */
|
||||
if (backlog == 0)
|
||||
backlog = 1;
|
||||
else if (backlog < 0)
|
||||
backlog = SOMAXCONN;
|
||||
#endif
|
||||
|
||||
if (listen(uv__stream_fd(handle), backlog))
|
||||
return -errno;
|
||||
|
||||
handle->connection_cb = cb;
|
||||
handle->io_watcher.cb = uv__server_io;
|
||||
uv__io_start(handle->loop, &handle->io_watcher, POLLIN);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void uv__pipe_close(uv_pipe_t* handle) {
|
||||
if (handle->pipe_fname) {
|
||||
/*
|
||||
* Unlink the file system entity before closing the file descriptor.
|
||||
* Doing it the other way around introduces a race where our process
|
||||
* unlinks a socket with the same name that's just been created by
|
||||
* another thread or process.
|
||||
*/
|
||||
unlink(handle->pipe_fname);
|
||||
uv__free((void*)handle->pipe_fname);
|
||||
handle->pipe_fname = NULL;
|
||||
}
|
||||
|
||||
uv__stream_close((uv_stream_t*)handle);
|
||||
}
|
||||
|
||||
|
||||
int uv_pipe_open(uv_pipe_t* handle, uv_file fd) {
|
||||
int err;
|
||||
|
||||
err = uv__nonblock(fd, 1);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
#if defined(__APPLE__)
|
||||
err = uv__stream_try_select((uv_stream_t*) handle, &fd);
|
||||
if (err)
|
||||
return err;
|
||||
#endif /* defined(__APPLE__) */
|
||||
|
||||
return uv__stream_open((uv_stream_t*)handle,
|
||||
fd,
|
||||
UV_STREAM_READABLE | UV_STREAM_WRITABLE);
|
||||
}
|
||||
|
||||
|
||||
void uv_pipe_connect(uv_connect_t* req,
|
||||
uv_pipe_t* handle,
|
||||
const char* name,
|
||||
uv_connect_cb cb) {
|
||||
struct sockaddr_un saddr;
|
||||
int new_sock;
|
||||
int err;
|
||||
int r;
|
||||
|
||||
new_sock = (uv__stream_fd(handle) == -1);
|
||||
|
||||
if (new_sock) {
|
||||
err = uv__socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
handle->io_watcher.fd = err;
|
||||
}
|
||||
|
||||
memset(&saddr, 0, sizeof saddr);
|
||||
strncpy(saddr.sun_path, name, sizeof(saddr.sun_path) - 1);
|
||||
saddr.sun_path[sizeof(saddr.sun_path) - 1] = '\0';
|
||||
saddr.sun_family = AF_UNIX;
|
||||
|
||||
do {
|
||||
r = connect(uv__stream_fd(handle),
|
||||
(struct sockaddr*)&saddr, sizeof saddr);
|
||||
}
|
||||
while (r == -1 && errno == EINTR);
|
||||
|
||||
if (r == -1 && errno != EINPROGRESS) {
|
||||
err = -errno;
|
||||
#if defined(__CYGWIN__) || defined(__MSYS__)
|
||||
/* EBADF is supposed to mean that the socket fd is bad, but
|
||||
Cygwin reports EBADF instead of ENOTSOCK when the file is
|
||||
not a socket. We do not expect to see a bad fd here
|
||||
(e.g. due to new_sock), so translate the error. */
|
||||
if (err == -EBADF)
|
||||
err = -ENOTSOCK;
|
||||
#endif
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = 0;
|
||||
if (new_sock) {
|
||||
err = uv__stream_open((uv_stream_t*)handle,
|
||||
uv__stream_fd(handle),
|
||||
UV_STREAM_READABLE | UV_STREAM_WRITABLE);
|
||||
}
|
||||
|
||||
if (err == 0)
|
||||
uv__io_start(handle->loop, &handle->io_watcher, POLLIN | POLLOUT);
|
||||
|
||||
out:
|
||||
handle->delayed_error = err;
|
||||
handle->connect_req = req;
|
||||
|
||||
uv__req_init(handle->loop, req, UV_CONNECT);
|
||||
req->handle = (uv_stream_t*)handle;
|
||||
req->cb = cb;
|
||||
QUEUE_INIT(&req->queue);
|
||||
|
||||
/* Force callback to run on next tick in case of error. */
|
||||
if (err)
|
||||
uv__io_feed(handle->loop, &handle->io_watcher);
|
||||
|
||||
}
|
||||
|
||||
|
||||
typedef int (*uv__peersockfunc)(int, struct sockaddr*, socklen_t*);
|
||||
|
||||
|
||||
static int uv__pipe_getsockpeername(const uv_pipe_t* handle,
|
||||
uv__peersockfunc func,
|
||||
char* buffer,
|
||||
size_t* size) {
|
||||
struct sockaddr_un sa;
|
||||
socklen_t addrlen;
|
||||
int err;
|
||||
|
||||
addrlen = sizeof(sa);
|
||||
memset(&sa, 0, addrlen);
|
||||
err = func(uv__stream_fd(handle), (struct sockaddr*) &sa, &addrlen);
|
||||
if (err < 0) {
|
||||
*size = 0;
|
||||
return -errno;
|
||||
}
|
||||
|
||||
#if defined(__linux__)
|
||||
if (sa.sun_path[0] == 0)
|
||||
/* Linux abstract namespace */
|
||||
addrlen -= offsetof(struct sockaddr_un, sun_path);
|
||||
else
|
||||
#endif
|
||||
addrlen = strlen(sa.sun_path);
|
||||
|
||||
|
||||
if (addrlen >= *size) {
|
||||
*size = addrlen + 1;
|
||||
return UV_ENOBUFS;
|
||||
}
|
||||
|
||||
memcpy(buffer, sa.sun_path, addrlen);
|
||||
*size = addrlen;
|
||||
|
||||
/* only null-terminate if it's not an abstract socket */
|
||||
if (buffer[0] != '\0')
|
||||
buffer[addrlen] = '\0';
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv_pipe_getsockname(const uv_pipe_t* handle, char* buffer, size_t* size) {
|
||||
return uv__pipe_getsockpeername(handle, getsockname, buffer, size);
|
||||
}
|
||||
|
||||
|
||||
int uv_pipe_getpeername(const uv_pipe_t* handle, char* buffer, size_t* size) {
|
||||
return uv__pipe_getsockpeername(handle, getpeername, buffer, size);
|
||||
}
|
||||
|
||||
|
||||
void uv_pipe_pending_instances(uv_pipe_t* handle, int count) {
|
||||
}
|
||||
|
||||
|
||||
int uv_pipe_pending_count(uv_pipe_t* handle) {
|
||||
uv__stream_queued_fds_t* queued_fds;
|
||||
|
||||
if (!handle->ipc)
|
||||
return 0;
|
||||
|
||||
if (handle->accepted_fd == -1)
|
||||
return 0;
|
||||
|
||||
if (handle->queued_fds == NULL)
|
||||
return 1;
|
||||
|
||||
queued_fds = handle->queued_fds;
|
||||
return queued_fds->offset + 1;
|
||||
}
|
||||
|
||||
|
||||
uv_handle_type uv_pipe_pending_type(uv_pipe_t* handle) {
|
||||
if (!handle->ipc)
|
||||
return UV_UNKNOWN_HANDLE;
|
||||
|
||||
if (handle->accepted_fd == -1)
|
||||
return UV_UNKNOWN_HANDLE;
|
||||
else
|
||||
return uv__handle_type(handle->accepted_fd);
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "uv.h"
|
||||
#include "internal.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
|
||||
|
||||
static void uv__poll_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) {
|
||||
uv_poll_t* handle;
|
||||
int pevents;
|
||||
|
||||
handle = container_of(w, uv_poll_t, io_watcher);
|
||||
|
||||
/*
|
||||
* As documented in the kernel source fs/kernfs/file.c #780
|
||||
* poll will return POLLERR|POLLPRI in case of sysfs
|
||||
* polling. This does not happen in case of out-of-band
|
||||
* TCP messages.
|
||||
*
|
||||
* The above is the case on (at least) FreeBSD and Linux.
|
||||
*
|
||||
* So to properly determine a POLLPRI or a POLLERR we need
|
||||
* to check for both.
|
||||
*/
|
||||
if ((events & POLLERR) && !(events & UV__POLLPRI)) {
|
||||
uv__io_stop(loop, w, POLLIN | POLLOUT | UV__POLLRDHUP | UV__POLLPRI);
|
||||
uv__handle_stop(handle);
|
||||
handle->poll_cb(handle, -EBADF, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
pevents = 0;
|
||||
if (events & POLLIN)
|
||||
pevents |= UV_READABLE;
|
||||
if (events & UV__POLLPRI)
|
||||
pevents |= UV_PRIORITIZED;
|
||||
if (events & POLLOUT)
|
||||
pevents |= UV_WRITABLE;
|
||||
if (events & UV__POLLRDHUP)
|
||||
pevents |= UV_DISCONNECT;
|
||||
|
||||
handle->poll_cb(handle, 0, pevents);
|
||||
}
|
||||
|
||||
|
||||
int uv_poll_init(uv_loop_t* loop, uv_poll_t* handle, int fd) {
|
||||
int err;
|
||||
|
||||
err = uv__io_check_fd(loop, fd);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* If ioctl(FIONBIO) reports ENOTTY, try fcntl(F_GETFL) + fcntl(F_SETFL).
|
||||
* Workaround for e.g. kqueue fds not supporting ioctls.
|
||||
*/
|
||||
err = uv__nonblock(fd, 1);
|
||||
if (err == -ENOTTY)
|
||||
if (uv__nonblock == uv__nonblock_ioctl)
|
||||
err = uv__nonblock_fcntl(fd, 1);
|
||||
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
uv__handle_init(loop, (uv_handle_t*) handle, UV_POLL);
|
||||
uv__io_init(&handle->io_watcher, uv__poll_io, fd);
|
||||
handle->poll_cb = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv_poll_init_socket(uv_loop_t* loop, uv_poll_t* handle,
|
||||
uv_os_sock_t socket) {
|
||||
return uv_poll_init(loop, handle, socket);
|
||||
}
|
||||
|
||||
|
||||
static void uv__poll_stop(uv_poll_t* handle) {
|
||||
uv__io_stop(handle->loop,
|
||||
&handle->io_watcher,
|
||||
POLLIN | POLLOUT | UV__POLLRDHUP | UV__POLLPRI);
|
||||
uv__handle_stop(handle);
|
||||
}
|
||||
|
||||
|
||||
int uv_poll_stop(uv_poll_t* handle) {
|
||||
assert(!uv__is_closing(handle));
|
||||
uv__poll_stop(handle);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv_poll_start(uv_poll_t* handle, int pevents, uv_poll_cb poll_cb) {
|
||||
int events;
|
||||
|
||||
assert((pevents & ~(UV_READABLE | UV_WRITABLE | UV_DISCONNECT |
|
||||
UV_PRIORITIZED)) == 0);
|
||||
assert(!uv__is_closing(handle));
|
||||
|
||||
uv__poll_stop(handle);
|
||||
|
||||
if (pevents == 0)
|
||||
return 0;
|
||||
|
||||
events = 0;
|
||||
if (pevents & UV_READABLE)
|
||||
events |= POLLIN;
|
||||
if (pevents & UV_PRIORITIZED)
|
||||
events |= UV__POLLPRI;
|
||||
if (pevents & UV_WRITABLE)
|
||||
events |= POLLOUT;
|
||||
if (pevents & UV_DISCONNECT)
|
||||
events |= UV__POLLRDHUP;
|
||||
|
||||
uv__io_start(handle->loop, &handle->io_watcher, events);
|
||||
uv__handle_start(handle);
|
||||
handle->poll_cb = poll_cb;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void uv__poll_close(uv_poll_t* handle) {
|
||||
uv__poll_stop(handle);
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/* Copyright libuv project contributors. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "uv.h"
|
||||
#include "internal.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <time.h>
|
||||
|
||||
#undef NANOSEC
|
||||
#define NANOSEC ((uint64_t) 1e9)
|
||||
|
||||
uint64_t uv__hrtime(uv_clocktype_t type) {
|
||||
struct timespec ts;
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
return (((uint64_t) ts.tv_sec) * NANOSEC + ts.tv_nsec);
|
||||
}
|
||||
@@ -0,0 +1,357 @@
|
||||
/* Copyright libuv project contributors. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "uv.h"
|
||||
#include "internal.h"
|
||||
|
||||
/* POSIX defines poll() as a portable way to wait on file descriptors.
|
||||
* Here we maintain a dynamically sized array of file descriptors and
|
||||
* events to pass as the first argument to poll().
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int uv__platform_loop_init(uv_loop_t* loop) {
|
||||
loop->poll_fds = NULL;
|
||||
loop->poll_fds_used = 0;
|
||||
loop->poll_fds_size = 0;
|
||||
loop->poll_fds_iterating = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void uv__platform_loop_delete(uv_loop_t* loop) {
|
||||
uv__free(loop->poll_fds);
|
||||
loop->poll_fds = NULL;
|
||||
}
|
||||
|
||||
int uv__io_fork(uv_loop_t* loop) {
|
||||
uv__platform_loop_delete(loop);
|
||||
return uv__platform_loop_init(loop);
|
||||
}
|
||||
|
||||
/* Allocate or dynamically resize our poll fds array. */
|
||||
static void uv__pollfds_maybe_resize(uv_loop_t* loop) {
|
||||
size_t i;
|
||||
size_t n;
|
||||
struct pollfd* p;
|
||||
|
||||
if (loop->poll_fds_used < loop->poll_fds_size)
|
||||
return;
|
||||
|
||||
n = loop->poll_fds_size ? loop->poll_fds_size * 2 : 64;
|
||||
p = uv__realloc(loop->poll_fds, n * sizeof(*loop->poll_fds));
|
||||
if (p == NULL)
|
||||
abort();
|
||||
|
||||
loop->poll_fds = p;
|
||||
for (i = loop->poll_fds_size; i < n; i++) {
|
||||
loop->poll_fds[i].fd = -1;
|
||||
loop->poll_fds[i].events = 0;
|
||||
loop->poll_fds[i].revents = 0;
|
||||
}
|
||||
loop->poll_fds_size = n;
|
||||
}
|
||||
|
||||
/* Primitive swap operation on poll fds array elements. */
|
||||
static void uv__pollfds_swap(uv_loop_t* loop, size_t l, size_t r) {
|
||||
struct pollfd pfd;
|
||||
pfd = loop->poll_fds[l];
|
||||
loop->poll_fds[l] = loop->poll_fds[r];
|
||||
loop->poll_fds[r] = pfd;
|
||||
}
|
||||
|
||||
/* Add a watcher's fd to our poll fds array with its pending events. */
|
||||
static void uv__pollfds_add(uv_loop_t* loop, uv__io_t* w) {
|
||||
size_t i;
|
||||
struct pollfd* pe;
|
||||
|
||||
/* If the fd is already in the set just update its events. */
|
||||
assert(!loop->poll_fds_iterating);
|
||||
for (i = 0; i < loop->poll_fds_used; ++i) {
|
||||
if (loop->poll_fds[i].fd == w->fd) {
|
||||
loop->poll_fds[i].events = w->pevents;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Otherwise, allocate a new slot in the set for the fd. */
|
||||
uv__pollfds_maybe_resize(loop);
|
||||
pe = &loop->poll_fds[loop->poll_fds_used++];
|
||||
pe->fd = w->fd;
|
||||
pe->events = w->pevents;
|
||||
}
|
||||
|
||||
/* Remove a watcher's fd from our poll fds array. */
|
||||
static void uv__pollfds_del(uv_loop_t* loop, int fd) {
|
||||
size_t i;
|
||||
assert(!loop->poll_fds_iterating);
|
||||
for (i = 0; i < loop->poll_fds_used; ++i) {
|
||||
if (loop->poll_fds[i].fd == fd) {
|
||||
/* swap to last position and remove */
|
||||
--loop->poll_fds_used;
|
||||
uv__pollfds_swap(loop, i, loop->poll_fds_used);
|
||||
loop->poll_fds[loop->poll_fds_used].fd = -1;
|
||||
loop->poll_fds[loop->poll_fds_used].events = 0;
|
||||
loop->poll_fds[loop->poll_fds_used].revents = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void uv__io_poll(uv_loop_t* loop, int timeout) {
|
||||
sigset_t* pset;
|
||||
sigset_t set;
|
||||
uint64_t time_base;
|
||||
uint64_t time_diff;
|
||||
QUEUE* q;
|
||||
uv__io_t* w;
|
||||
size_t i;
|
||||
unsigned int nevents;
|
||||
int nfds;
|
||||
int have_signals;
|
||||
struct pollfd* pe;
|
||||
int fd;
|
||||
|
||||
if (loop->nfds == 0) {
|
||||
assert(QUEUE_EMPTY(&loop->watcher_queue));
|
||||
return;
|
||||
}
|
||||
|
||||
/* Take queued watchers and add their fds to our poll fds array. */
|
||||
while (!QUEUE_EMPTY(&loop->watcher_queue)) {
|
||||
q = QUEUE_HEAD(&loop->watcher_queue);
|
||||
QUEUE_REMOVE(q);
|
||||
QUEUE_INIT(q);
|
||||
|
||||
w = QUEUE_DATA(q, uv__io_t, watcher_queue);
|
||||
assert(w->pevents != 0);
|
||||
assert(w->fd >= 0);
|
||||
assert(w->fd < (int) loop->nwatchers);
|
||||
|
||||
uv__pollfds_add(loop, w);
|
||||
|
||||
w->events = w->pevents;
|
||||
}
|
||||
|
||||
/* Prepare a set of signals to block around poll(), if any. */
|
||||
pset = NULL;
|
||||
if (loop->flags & UV_LOOP_BLOCK_SIGPROF) {
|
||||
pset = &set;
|
||||
sigemptyset(pset);
|
||||
sigaddset(pset, SIGPROF);
|
||||
}
|
||||
|
||||
assert(timeout >= -1);
|
||||
time_base = loop->time;
|
||||
|
||||
/* Loop calls to poll() and processing of results. If we get some
|
||||
* results from poll() but they turn out not to be interesting to
|
||||
* our caller then we need to loop around and poll() again.
|
||||
*/
|
||||
for (;;) {
|
||||
if (pset != NULL)
|
||||
if (pthread_sigmask(SIG_BLOCK, pset, NULL))
|
||||
abort();
|
||||
nfds = poll(loop->poll_fds, (nfds_t)loop->poll_fds_used, timeout);
|
||||
if (pset != NULL)
|
||||
if (pthread_sigmask(SIG_UNBLOCK, pset, NULL))
|
||||
abort();
|
||||
|
||||
/* Update loop->time unconditionally. It's tempting to skip the update when
|
||||
* timeout == 0 (i.e. non-blocking poll) but there is no guarantee that the
|
||||
* operating system didn't reschedule our process while in the syscall.
|
||||
*/
|
||||
SAVE_ERRNO(uv__update_time(loop));
|
||||
|
||||
if (nfds == 0) {
|
||||
assert(timeout != -1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (nfds == -1) {
|
||||
if (errno != EINTR)
|
||||
abort();
|
||||
|
||||
if (timeout == -1)
|
||||
continue;
|
||||
|
||||
if (timeout == 0)
|
||||
return;
|
||||
|
||||
/* Interrupted by a signal. Update timeout and poll again. */
|
||||
goto update_timeout;
|
||||
}
|
||||
|
||||
/* Tell uv__platform_invalidate_fd not to manipulate our array
|
||||
* while we are iterating over it.
|
||||
*/
|
||||
loop->poll_fds_iterating = 1;
|
||||
|
||||
/* Initialize a count of events that we care about. */
|
||||
nevents = 0;
|
||||
have_signals = 0;
|
||||
|
||||
/* Loop over the entire poll fds array looking for returned events. */
|
||||
for (i = 0; i < loop->poll_fds_used; i++) {
|
||||
pe = loop->poll_fds + i;
|
||||
fd = pe->fd;
|
||||
|
||||
/* Skip invalidated events, see uv__platform_invalidate_fd. */
|
||||
if (fd == -1)
|
||||
continue;
|
||||
|
||||
assert(fd >= 0);
|
||||
assert((unsigned) fd < loop->nwatchers);
|
||||
|
||||
w = loop->watchers[fd];
|
||||
|
||||
if (w == NULL) {
|
||||
/* File descriptor that we've stopped watching, ignore. */
|
||||
uv__platform_invalidate_fd(loop, fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Filter out events that user has not requested us to watch
|
||||
* (e.g. POLLNVAL).
|
||||
*/
|
||||
pe->revents &= w->pevents | POLLERR | POLLHUP;
|
||||
|
||||
if (pe->revents != 0) {
|
||||
/* Run signal watchers last. */
|
||||
if (w == &loop->signal_io_watcher) {
|
||||
have_signals = 1;
|
||||
} else {
|
||||
w->cb(loop, w, pe->revents);
|
||||
}
|
||||
|
||||
nevents++;
|
||||
}
|
||||
}
|
||||
|
||||
if (have_signals != 0)
|
||||
loop->signal_io_watcher.cb(loop, &loop->signal_io_watcher, POLLIN);
|
||||
|
||||
loop->poll_fds_iterating = 0;
|
||||
|
||||
/* Purge invalidated fds from our poll fds array. */
|
||||
uv__pollfds_del(loop, -1);
|
||||
|
||||
if (have_signals != 0)
|
||||
return; /* Event loop should cycle now so don't poll again. */
|
||||
|
||||
if (nevents != 0)
|
||||
return;
|
||||
|
||||
if (timeout == 0)
|
||||
return;
|
||||
|
||||
if (timeout == -1)
|
||||
continue;
|
||||
|
||||
update_timeout:
|
||||
assert(timeout > 0);
|
||||
|
||||
time_diff = loop->time - time_base;
|
||||
if (time_diff >= (uint64_t) timeout)
|
||||
return;
|
||||
|
||||
timeout -= time_diff;
|
||||
}
|
||||
}
|
||||
|
||||
/* Remove the given fd from our poll fds array because no one
|
||||
* is interested in its events anymore.
|
||||
*/
|
||||
void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) {
|
||||
size_t i;
|
||||
|
||||
if (loop->poll_fds_iterating) {
|
||||
/* uv__io_poll is currently iterating. Just invalidate fd. */
|
||||
for (i = 0; i < loop->poll_fds_used; i++)
|
||||
if (loop->poll_fds[i].fd == fd) {
|
||||
loop->poll_fds[i].fd = -1;
|
||||
loop->poll_fds[i].events = 0;
|
||||
loop->poll_fds[i].revents = 0;
|
||||
}
|
||||
} else {
|
||||
/* uv__io_poll is not iterating. Delete fd from the set. */
|
||||
uv__pollfds_del(loop, fd);
|
||||
}
|
||||
}
|
||||
|
||||
/* Check whether the given fd is supported by poll(). */
|
||||
int uv__io_check_fd(uv_loop_t* loop, int fd) {
|
||||
struct pollfd p[1];
|
||||
int rv;
|
||||
|
||||
p[0].fd = fd;
|
||||
p[0].events = POLLIN;
|
||||
|
||||
do
|
||||
rv = poll(p, 1, 0);
|
||||
while (rv == -1 && (errno == EINTR || errno == EAGAIN));
|
||||
|
||||
if (rv == -1)
|
||||
return -errno;
|
||||
|
||||
if (p[0].revents & POLLNVAL)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* NEW
|
||||
*/
|
||||
int uv_fs_event_stop(uv_fs_event_t* handle) {
|
||||
if (!uv__is_active(handle))
|
||||
return 0;
|
||||
|
||||
uv__handle_stop(handle);
|
||||
|
||||
#if defined(__APPLE__)
|
||||
if (uv__has_forked_with_cfrunloop || uv__fsevents_close(handle))
|
||||
#endif /* defined(__APPLE__) */
|
||||
{
|
||||
uv__io_close(handle->loop, &handle->event_watcher);
|
||||
}
|
||||
|
||||
uv__free(handle->path);
|
||||
handle->path = NULL;
|
||||
|
||||
if (handle->event_watcher.fd != -1) {
|
||||
/* When FSEvents is used, we don't use the event_watcher's fd under certain
|
||||
* confitions. (see uv_fs_event_start) */
|
||||
uv__close(handle->event_watcher.fd);
|
||||
handle->event_watcher.fd = -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void uv__fs_event_close(uv_fs_event_t* handle) {
|
||||
uv_fs_event_stop(handle);
|
||||
}
|
||||
@@ -0,0 +1,563 @@
|
||||
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "uv.h"
|
||||
#include "internal.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <poll.h>
|
||||
|
||||
#if defined(__APPLE__) && !TARGET_OS_IPHONE
|
||||
# include <crt_externs.h>
|
||||
# define environ (*_NSGetEnviron())
|
||||
#else
|
||||
extern char **environ;
|
||||
#endif
|
||||
|
||||
#if defined(__linux__) || defined(__GLIBC__)
|
||||
# include <grp.h>
|
||||
#endif
|
||||
|
||||
|
||||
static void uv__chld(uv_signal_t* handle, int signum) {
|
||||
uv_process_t* process;
|
||||
uv_loop_t* loop;
|
||||
int exit_status;
|
||||
int term_signal;
|
||||
int status;
|
||||
pid_t pid;
|
||||
QUEUE pending;
|
||||
QUEUE* q;
|
||||
QUEUE* h;
|
||||
|
||||
assert(signum == SIGCHLD);
|
||||
|
||||
QUEUE_INIT(&pending);
|
||||
loop = handle->loop;
|
||||
|
||||
h = &loop->process_handles;
|
||||
q = QUEUE_HEAD(h);
|
||||
while (q != h) {
|
||||
process = QUEUE_DATA(q, uv_process_t, queue);
|
||||
q = QUEUE_NEXT(q);
|
||||
|
||||
do
|
||||
pid = waitpid(process->pid, &status, WNOHANG);
|
||||
while (pid == -1 && errno == EINTR);
|
||||
|
||||
if (pid == 0)
|
||||
continue;
|
||||
|
||||
if (pid == -1) {
|
||||
if (errno != ECHILD)
|
||||
abort();
|
||||
continue;
|
||||
}
|
||||
|
||||
process->status = status;
|
||||
QUEUE_REMOVE(&process->queue);
|
||||
QUEUE_INSERT_TAIL(&pending, &process->queue);
|
||||
}
|
||||
|
||||
h = &pending;
|
||||
q = QUEUE_HEAD(h);
|
||||
while (q != h) {
|
||||
process = QUEUE_DATA(q, uv_process_t, queue);
|
||||
q = QUEUE_NEXT(q);
|
||||
|
||||
QUEUE_REMOVE(&process->queue);
|
||||
QUEUE_INIT(&process->queue);
|
||||
uv__handle_stop(process);
|
||||
|
||||
if (process->exit_cb == NULL)
|
||||
continue;
|
||||
|
||||
exit_status = 0;
|
||||
if (WIFEXITED(process->status))
|
||||
exit_status = WEXITSTATUS(process->status);
|
||||
|
||||
term_signal = 0;
|
||||
if (WIFSIGNALED(process->status))
|
||||
term_signal = WTERMSIG(process->status);
|
||||
|
||||
process->exit_cb(process, exit_status, term_signal);
|
||||
}
|
||||
assert(QUEUE_EMPTY(&pending));
|
||||
}
|
||||
|
||||
|
||||
int uv__make_socketpair(int fds[2], int flags) {
|
||||
#if defined(__linux__)
|
||||
static int no_cloexec;
|
||||
|
||||
if (no_cloexec)
|
||||
goto skip;
|
||||
|
||||
if (socketpair(AF_UNIX, SOCK_STREAM | UV__SOCK_CLOEXEC | flags, 0, fds) == 0)
|
||||
return 0;
|
||||
|
||||
/* Retry on EINVAL, it means SOCK_CLOEXEC is not supported.
|
||||
* Anything else is a genuine error.
|
||||
*/
|
||||
if (errno != EINVAL)
|
||||
return -errno;
|
||||
|
||||
no_cloexec = 1;
|
||||
|
||||
skip:
|
||||
#endif
|
||||
|
||||
if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds))
|
||||
return -errno;
|
||||
|
||||
uv__cloexec(fds[0], 1);
|
||||
uv__cloexec(fds[1], 1);
|
||||
|
||||
if (flags & UV__F_NONBLOCK) {
|
||||
uv__nonblock(fds[0], 1);
|
||||
uv__nonblock(fds[1], 1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv__make_pipe(int fds[2], int flags) {
|
||||
#if defined(__linux__)
|
||||
static int no_pipe2;
|
||||
|
||||
if (no_pipe2)
|
||||
goto skip;
|
||||
|
||||
if (uv__pipe2(fds, flags | UV__O_CLOEXEC) == 0)
|
||||
return 0;
|
||||
|
||||
if (errno != ENOSYS)
|
||||
return -errno;
|
||||
|
||||
no_pipe2 = 1;
|
||||
|
||||
skip:
|
||||
#endif
|
||||
|
||||
if (pipe(fds))
|
||||
return -errno;
|
||||
|
||||
uv__cloexec(fds[0], 1);
|
||||
uv__cloexec(fds[1], 1);
|
||||
|
||||
if (flags & UV__F_NONBLOCK) {
|
||||
uv__nonblock(fds[0], 1);
|
||||
uv__nonblock(fds[1], 1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Used for initializing stdio streams like options.stdin_stream. Returns
|
||||
* zero on success. See also the cleanup section in uv_spawn().
|
||||
*/
|
||||
static int uv__process_init_stdio(uv_stdio_container_t* container, int fds[2]) {
|
||||
int mask;
|
||||
int fd;
|
||||
|
||||
mask = UV_IGNORE | UV_CREATE_PIPE | UV_INHERIT_FD | UV_INHERIT_STREAM;
|
||||
|
||||
switch (container->flags & mask) {
|
||||
case UV_IGNORE:
|
||||
return 0;
|
||||
|
||||
case UV_CREATE_PIPE:
|
||||
assert(container->data.stream != NULL);
|
||||
if (container->data.stream->type != UV_NAMED_PIPE)
|
||||
return -EINVAL;
|
||||
else
|
||||
return uv__make_socketpair(fds, 0);
|
||||
|
||||
case UV_INHERIT_FD:
|
||||
case UV_INHERIT_STREAM:
|
||||
if (container->flags & UV_INHERIT_FD)
|
||||
fd = container->data.fd;
|
||||
else
|
||||
fd = uv__stream_fd(container->data.stream);
|
||||
|
||||
if (fd == -1)
|
||||
return -EINVAL;
|
||||
|
||||
fds[1] = fd;
|
||||
return 0;
|
||||
|
||||
default:
|
||||
assert(0 && "Unexpected flags");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int uv__process_open_stream(uv_stdio_container_t* container,
|
||||
int pipefds[2],
|
||||
int writable) {
|
||||
int flags;
|
||||
int err;
|
||||
|
||||
if (!(container->flags & UV_CREATE_PIPE) || pipefds[0] < 0)
|
||||
return 0;
|
||||
|
||||
err = uv__close(pipefds[1]);
|
||||
if (err != 0)
|
||||
abort();
|
||||
|
||||
pipefds[1] = -1;
|
||||
uv__nonblock(pipefds[0], 1);
|
||||
|
||||
if (container->data.stream->type == UV_NAMED_PIPE &&
|
||||
((uv_pipe_t*)container->data.stream)->ipc)
|
||||
flags = UV_STREAM_READABLE | UV_STREAM_WRITABLE;
|
||||
else if (writable)
|
||||
flags = UV_STREAM_WRITABLE;
|
||||
else
|
||||
flags = UV_STREAM_READABLE;
|
||||
|
||||
return uv__stream_open(container->data.stream, pipefds[0], flags);
|
||||
}
|
||||
|
||||
|
||||
static void uv__process_close_stream(uv_stdio_container_t* container) {
|
||||
if (!(container->flags & UV_CREATE_PIPE)) return;
|
||||
uv__stream_close((uv_stream_t*)container->data.stream);
|
||||
}
|
||||
|
||||
|
||||
static void uv__write_int(int fd, int val) {
|
||||
ssize_t n;
|
||||
|
||||
do
|
||||
n = write(fd, &val, sizeof(val));
|
||||
while (n == -1 && errno == EINTR);
|
||||
|
||||
if (n == -1 && errno == EPIPE)
|
||||
return; /* parent process has quit */
|
||||
|
||||
assert(n == sizeof(val));
|
||||
}
|
||||
|
||||
|
||||
#if !(defined(__APPLE__) && (TARGET_OS_TV || TARGET_OS_WATCH))
|
||||
/* execvp is marked __WATCHOS_PROHIBITED __TVOS_PROHIBITED, so must be
|
||||
* avoided. Since this isn't called on those targets, the function
|
||||
* doesn't even need to be defined for them.
|
||||
*/
|
||||
static void uv__process_child_init(const uv_process_options_t* options,
|
||||
int stdio_count,
|
||||
int (*pipes)[2],
|
||||
int error_fd) {
|
||||
int close_fd;
|
||||
int use_fd;
|
||||
int fd;
|
||||
|
||||
if (options->flags & UV_PROCESS_DETACHED)
|
||||
setsid();
|
||||
|
||||
/* First duplicate low numbered fds, since it's not safe to duplicate them,
|
||||
* they could get replaced. Example: swapping stdout and stderr; without
|
||||
* this fd 2 (stderr) would be duplicated into fd 1, thus making both
|
||||
* stdout and stderr go to the same fd, which was not the intention. */
|
||||
for (fd = 0; fd < stdio_count; fd++) {
|
||||
use_fd = pipes[fd][1];
|
||||
if (use_fd < 0 || use_fd >= fd)
|
||||
continue;
|
||||
pipes[fd][1] = fcntl(use_fd, F_DUPFD, stdio_count);
|
||||
if (pipes[fd][1] == -1) {
|
||||
uv__write_int(error_fd, -errno);
|
||||
_exit(127);
|
||||
}
|
||||
}
|
||||
|
||||
for (fd = 0; fd < stdio_count; fd++) {
|
||||
close_fd = pipes[fd][0];
|
||||
use_fd = pipes[fd][1];
|
||||
|
||||
if (use_fd < 0) {
|
||||
if (fd >= 3)
|
||||
continue;
|
||||
else {
|
||||
/* redirect stdin, stdout and stderr to /dev/null even if UV_IGNORE is
|
||||
* set
|
||||
*/
|
||||
use_fd = open("/dev/null", fd == 0 ? O_RDONLY : O_RDWR);
|
||||
close_fd = use_fd;
|
||||
|
||||
if (use_fd == -1) {
|
||||
uv__write_int(error_fd, -errno);
|
||||
_exit(127);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (fd == use_fd)
|
||||
uv__cloexec_fcntl(use_fd, 0);
|
||||
else
|
||||
fd = dup2(use_fd, fd);
|
||||
|
||||
if (fd == -1) {
|
||||
uv__write_int(error_fd, -errno);
|
||||
_exit(127);
|
||||
}
|
||||
|
||||
if (fd <= 2)
|
||||
uv__nonblock_fcntl(fd, 0);
|
||||
|
||||
if (close_fd >= stdio_count)
|
||||
uv__close(close_fd);
|
||||
}
|
||||
|
||||
for (fd = 0; fd < stdio_count; fd++) {
|
||||
use_fd = pipes[fd][1];
|
||||
|
||||
if (use_fd >= stdio_count)
|
||||
uv__close(use_fd);
|
||||
}
|
||||
|
||||
if (options->cwd != NULL && chdir(options->cwd)) {
|
||||
uv__write_int(error_fd, -errno);
|
||||
_exit(127);
|
||||
}
|
||||
|
||||
if (options->flags & (UV_PROCESS_SETUID | UV_PROCESS_SETGID)) {
|
||||
/* When dropping privileges from root, the `setgroups` call will
|
||||
* remove any extraneous groups. If we don't call this, then
|
||||
* even though our uid has dropped, we may still have groups
|
||||
* that enable us to do super-user things. This will fail if we
|
||||
* aren't root, so don't bother checking the return value, this
|
||||
* is just done as an optimistic privilege dropping function.
|
||||
*/
|
||||
SAVE_ERRNO(setgroups(0, NULL));
|
||||
}
|
||||
|
||||
if ((options->flags & UV_PROCESS_SETGID) && setgid(options->gid)) {
|
||||
uv__write_int(error_fd, -errno);
|
||||
_exit(127);
|
||||
}
|
||||
|
||||
if ((options->flags & UV_PROCESS_SETUID) && setuid(options->uid)) {
|
||||
uv__write_int(error_fd, -errno);
|
||||
_exit(127);
|
||||
}
|
||||
|
||||
if (options->env != NULL) {
|
||||
environ = options->env;
|
||||
}
|
||||
|
||||
execvp(options->file, options->args);
|
||||
uv__write_int(error_fd, -errno);
|
||||
_exit(127);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
int uv_spawn(uv_loop_t* loop,
|
||||
uv_process_t* process,
|
||||
const uv_process_options_t* options) {
|
||||
#if defined(__APPLE__) && (TARGET_OS_TV || TARGET_OS_WATCH)
|
||||
/* fork is marked __WATCHOS_PROHIBITED __TVOS_PROHIBITED. */
|
||||
return -ENOSYS;
|
||||
#else
|
||||
int signal_pipe[2] = { -1, -1 };
|
||||
int (*pipes)[2];
|
||||
int stdio_count;
|
||||
ssize_t r;
|
||||
pid_t pid;
|
||||
int err;
|
||||
int exec_errorno;
|
||||
int i;
|
||||
int status;
|
||||
|
||||
assert(options->file != NULL);
|
||||
assert(!(options->flags & ~(UV_PROCESS_DETACHED |
|
||||
UV_PROCESS_SETGID |
|
||||
UV_PROCESS_SETUID |
|
||||
UV_PROCESS_WINDOWS_HIDE |
|
||||
UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS)));
|
||||
|
||||
uv__handle_init(loop, (uv_handle_t*)process, UV_PROCESS);
|
||||
QUEUE_INIT(&process->queue);
|
||||
|
||||
stdio_count = options->stdio_count;
|
||||
if (stdio_count < 3)
|
||||
stdio_count = 3;
|
||||
|
||||
err = -ENOMEM;
|
||||
pipes = uv__malloc(stdio_count * sizeof(*pipes));
|
||||
if (pipes == NULL)
|
||||
goto error;
|
||||
|
||||
for (i = 0; i < stdio_count; i++) {
|
||||
pipes[i][0] = -1;
|
||||
pipes[i][1] = -1;
|
||||
}
|
||||
|
||||
for (i = 0; i < options->stdio_count; i++) {
|
||||
err = uv__process_init_stdio(options->stdio + i, pipes[i]);
|
||||
if (err)
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* This pipe is used by the parent to wait until
|
||||
* the child has called `execve()`. We need this
|
||||
* to avoid the following race condition:
|
||||
*
|
||||
* if ((pid = fork()) > 0) {
|
||||
* kill(pid, SIGTERM);
|
||||
* }
|
||||
* else if (pid == 0) {
|
||||
* execve("/bin/cat", argp, envp);
|
||||
* }
|
||||
*
|
||||
* The parent sends a signal immediately after forking.
|
||||
* Since the child may not have called `execve()` yet,
|
||||
* there is no telling what process receives the signal,
|
||||
* our fork or /bin/cat.
|
||||
*
|
||||
* To avoid ambiguity, we create a pipe with both ends
|
||||
* marked close-on-exec. Then, after the call to `fork()`,
|
||||
* the parent polls the read end until it EOFs or errors with EPIPE.
|
||||
*/
|
||||
err = uv__make_pipe(signal_pipe, 0);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
uv_signal_start(&loop->child_watcher, uv__chld, SIGCHLD);
|
||||
|
||||
/* Acquire write lock to prevent opening new fds in worker threads */
|
||||
uv_rwlock_wrlock(&loop->cloexec_lock);
|
||||
pid = fork();
|
||||
|
||||
if (pid == -1) {
|
||||
err = -errno;
|
||||
uv_rwlock_wrunlock(&loop->cloexec_lock);
|
||||
uv__close(signal_pipe[0]);
|
||||
uv__close(signal_pipe[1]);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (pid == 0) {
|
||||
uv__process_child_init(options, stdio_count, pipes, signal_pipe[1]);
|
||||
abort();
|
||||
}
|
||||
|
||||
/* Release lock in parent process */
|
||||
uv_rwlock_wrunlock(&loop->cloexec_lock);
|
||||
uv__close(signal_pipe[1]);
|
||||
|
||||
process->status = 0;
|
||||
exec_errorno = 0;
|
||||
do
|
||||
r = read(signal_pipe[0], &exec_errorno, sizeof(exec_errorno));
|
||||
while (r == -1 && errno == EINTR);
|
||||
|
||||
if (r == 0)
|
||||
; /* okay, EOF */
|
||||
else if (r == sizeof(exec_errorno)) {
|
||||
do
|
||||
err = waitpid(pid, &status, 0); /* okay, read errorno */
|
||||
while (err == -1 && errno == EINTR);
|
||||
assert(err == pid);
|
||||
} else if (r == -1 && errno == EPIPE) {
|
||||
do
|
||||
err = waitpid(pid, &status, 0); /* okay, got EPIPE */
|
||||
while (err == -1 && errno == EINTR);
|
||||
assert(err == pid);
|
||||
} else
|
||||
abort();
|
||||
|
||||
uv__close_nocheckstdio(signal_pipe[0]);
|
||||
|
||||
for (i = 0; i < options->stdio_count; i++) {
|
||||
err = uv__process_open_stream(options->stdio + i, pipes[i], i == 0);
|
||||
if (err == 0)
|
||||
continue;
|
||||
|
||||
while (i--)
|
||||
uv__process_close_stream(options->stdio + i);
|
||||
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Only activate this handle if exec() happened successfully */
|
||||
if (exec_errorno == 0) {
|
||||
QUEUE_INSERT_TAIL(&loop->process_handles, &process->queue);
|
||||
uv__handle_start(process);
|
||||
}
|
||||
|
||||
process->pid = pid;
|
||||
process->exit_cb = options->exit_cb;
|
||||
|
||||
uv__free(pipes);
|
||||
return exec_errorno;
|
||||
|
||||
error:
|
||||
if (pipes != NULL) {
|
||||
for (i = 0; i < stdio_count; i++) {
|
||||
if (i < options->stdio_count)
|
||||
if (options->stdio[i].flags & (UV_INHERIT_FD | UV_INHERIT_STREAM))
|
||||
continue;
|
||||
if (pipes[i][0] != -1)
|
||||
uv__close_nocheckstdio(pipes[i][0]);
|
||||
if (pipes[i][1] != -1)
|
||||
uv__close_nocheckstdio(pipes[i][1]);
|
||||
}
|
||||
uv__free(pipes);
|
||||
}
|
||||
|
||||
return err;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
int uv_process_kill(uv_process_t* process, int signum) {
|
||||
return uv_kill(process->pid, signum);
|
||||
}
|
||||
|
||||
|
||||
int uv_kill(int pid, int signum) {
|
||||
if (kill(pid, signum))
|
||||
return -errno;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void uv__process_close(uv_process_t* handle) {
|
||||
QUEUE_REMOVE(&handle->queue);
|
||||
uv__handle_stop(handle);
|
||||
if (QUEUE_EMPTY(&handle->loop->process_handles))
|
||||
uv_signal_stop(&handle->loop->child_watcher);
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/* Copyright libuv project contributors. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "uv.h"
|
||||
#include "internal.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int uv_exepath(char* buffer, size_t* size) {
|
||||
ssize_t n;
|
||||
|
||||
if (buffer == NULL || size == NULL || *size == 0)
|
||||
return -EINVAL;
|
||||
|
||||
n = *size - 1;
|
||||
if (n > 0)
|
||||
n = readlink("/proc/self/exe", buffer, n);
|
||||
|
||||
if (n == -1)
|
||||
return -errno;
|
||||
|
||||
buffer[n] = '\0';
|
||||
*size = n;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "uv.h"
|
||||
#include "internal.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
extern void uv__set_process_title(const char* title);
|
||||
|
||||
static void* args_mem;
|
||||
|
||||
static struct {
|
||||
char* str;
|
||||
size_t len;
|
||||
} process_title;
|
||||
|
||||
|
||||
char** uv_setup_args(int argc, char** argv) {
|
||||
char** new_argv;
|
||||
size_t size;
|
||||
char* s;
|
||||
int i;
|
||||
|
||||
if (argc <= 0)
|
||||
return argv;
|
||||
|
||||
/* Calculate how much memory we need for the argv strings. */
|
||||
size = 0;
|
||||
for (i = 0; i < argc; i++)
|
||||
size += strlen(argv[i]) + 1;
|
||||
|
||||
#if defined(__MVS__)
|
||||
/* argv is not adjacent. So just use argv[0] */
|
||||
process_title.str = argv[0];
|
||||
process_title.len = strlen(argv[0]);
|
||||
#else
|
||||
process_title.str = argv[0];
|
||||
process_title.len = argv[argc - 1] + strlen(argv[argc - 1]) - argv[0];
|
||||
assert(process_title.len + 1 == size); /* argv memory should be adjacent. */
|
||||
#endif
|
||||
|
||||
/* Add space for the argv pointers. */
|
||||
size += (argc + 1) * sizeof(char*);
|
||||
|
||||
new_argv = uv__malloc(size);
|
||||
if (new_argv == NULL)
|
||||
return argv;
|
||||
args_mem = new_argv;
|
||||
|
||||
/* Copy over the strings and set up the pointer table. */
|
||||
s = (char*) &new_argv[argc + 1];
|
||||
for (i = 0; i < argc; i++) {
|
||||
size = strlen(argv[i]) + 1;
|
||||
memcpy(s, argv[i], size);
|
||||
new_argv[i] = s;
|
||||
s += size;
|
||||
}
|
||||
new_argv[i] = NULL;
|
||||
|
||||
return new_argv;
|
||||
}
|
||||
|
||||
|
||||
int uv_set_process_title(const char* title) {
|
||||
if (process_title.len == 0)
|
||||
return 0;
|
||||
|
||||
/* No need to terminate, byte after is always '\0'. */
|
||||
strncpy(process_title.str, title, process_title.len);
|
||||
uv__set_process_title(title);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv_get_process_title(char* buffer, size_t size) {
|
||||
if (buffer == NULL || size == 0)
|
||||
return -EINVAL;
|
||||
else if (size <= process_title.len)
|
||||
return -ENOBUFS;
|
||||
|
||||
if (process_title.len != 0)
|
||||
memcpy(buffer, process_title.str, process_title.len + 1);
|
||||
|
||||
buffer[process_title.len] = '\0';
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
UV_DESTRUCTOR(static void free_args_mem(void)) {
|
||||
uv__free(args_mem); /* Keep valgrind happy. */
|
||||
args_mem = NULL;
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
Copyright (c) 2016, Kari Tristan Helgason <kthelgason@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
#include "uv-common.h"
|
||||
#include "pthread-barrier.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
|
||||
/* TODO: support barrier_attr */
|
||||
int pthread_barrier_init(pthread_barrier_t* barrier,
|
||||
const void* barrier_attr,
|
||||
unsigned count) {
|
||||
int rc;
|
||||
_uv_barrier* b;
|
||||
|
||||
if (barrier == NULL || count == 0)
|
||||
return EINVAL;
|
||||
|
||||
if (barrier_attr != NULL)
|
||||
return ENOTSUP;
|
||||
|
||||
b = uv__malloc(sizeof(*b));
|
||||
if (b == NULL)
|
||||
return ENOMEM;
|
||||
|
||||
b->in = 0;
|
||||
b->out = 0;
|
||||
b->threshold = count;
|
||||
|
||||
if ((rc = pthread_mutex_init(&b->mutex, NULL)) != 0)
|
||||
goto error2;
|
||||
if ((rc = pthread_cond_init(&b->cond, NULL)) != 0)
|
||||
goto error;
|
||||
|
||||
barrier->b = b;
|
||||
return 0;
|
||||
|
||||
error:
|
||||
pthread_mutex_destroy(&b->mutex);
|
||||
error2:
|
||||
uv__free(b);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int pthread_barrier_wait(pthread_barrier_t* barrier) {
|
||||
int rc;
|
||||
_uv_barrier* b;
|
||||
|
||||
if (barrier == NULL || barrier->b == NULL)
|
||||
return EINVAL;
|
||||
|
||||
b = barrier->b;
|
||||
/* Lock the mutex*/
|
||||
if ((rc = pthread_mutex_lock(&b->mutex)) != 0)
|
||||
return rc;
|
||||
|
||||
/* Increment the count. If this is the first thread to reach the threshold,
|
||||
wake up waiters, unlock the mutex, then return
|
||||
PTHREAD_BARRIER_SERIAL_THREAD. */
|
||||
if (++b->in == b->threshold) {
|
||||
b->in = 0;
|
||||
b->out = b->threshold - 1;
|
||||
rc = pthread_cond_signal(&b->cond);
|
||||
assert(rc == 0);
|
||||
|
||||
pthread_mutex_unlock(&b->mutex);
|
||||
return PTHREAD_BARRIER_SERIAL_THREAD;
|
||||
}
|
||||
/* Otherwise, wait for other threads until in is set to 0,
|
||||
then return 0 to indicate this is not the first thread. */
|
||||
do {
|
||||
if ((rc = pthread_cond_wait(&b->cond, &b->mutex)) != 0)
|
||||
break;
|
||||
} while (b->in != 0);
|
||||
|
||||
/* mark thread exit */
|
||||
b->out--;
|
||||
pthread_cond_signal(&b->cond);
|
||||
pthread_mutex_unlock(&b->mutex);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int pthread_barrier_destroy(pthread_barrier_t* barrier) {
|
||||
int rc;
|
||||
_uv_barrier* b;
|
||||
|
||||
if (barrier == NULL || barrier->b == NULL)
|
||||
return EINVAL;
|
||||
|
||||
b = barrier->b;
|
||||
|
||||
if ((rc = pthread_mutex_lock(&b->mutex)) != 0)
|
||||
return rc;
|
||||
|
||||
if (b->in > 0 || b->out > 0)
|
||||
rc = EBUSY;
|
||||
|
||||
pthread_mutex_unlock(&b->mutex);
|
||||
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
pthread_cond_destroy(&b->cond);
|
||||
pthread_mutex_destroy(&b->mutex);
|
||||
uv__free(barrier->b);
|
||||
barrier->b = NULL;
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/* Copyright (c) 2013, Sony Mobile Communications AB
|
||||
* Copyright (c) 2012, Google Inc.
|
||||
All rights reserved.
|
||||
|
||||
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 Google Inc. 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
|
||||
OWNER 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.
|
||||
*/
|
||||
|
||||
/* Android versions < 4.1 have a broken pthread_sigmask. */
|
||||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
|
||||
int uv__pthread_sigmask(int how, const sigset_t* set, sigset_t* oset) {
|
||||
static int workaround;
|
||||
int err;
|
||||
|
||||
if (workaround) {
|
||||
return sigprocmask(how, set, oset);
|
||||
} else {
|
||||
err = pthread_sigmask(how, set, oset);
|
||||
if (err) {
|
||||
if (err == EINVAL && sigprocmask(how, set, oset) == 0) {
|
||||
workaround = 1;
|
||||
return 0;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,559 @@
|
||||
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "uv.h"
|
||||
#include "internal.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
|
||||
typedef struct {
|
||||
uv_signal_t* handle;
|
||||
int signum;
|
||||
} uv__signal_msg_t;
|
||||
|
||||
RB_HEAD(uv__signal_tree_s, uv_signal_s);
|
||||
|
||||
|
||||
static int uv__signal_unlock(void);
|
||||
static int uv__signal_start(uv_signal_t* handle,
|
||||
uv_signal_cb signal_cb,
|
||||
int signum,
|
||||
int oneshot);
|
||||
static void uv__signal_event(uv_loop_t* loop, uv__io_t* w, unsigned int events);
|
||||
static int uv__signal_compare(uv_signal_t* w1, uv_signal_t* w2);
|
||||
static void uv__signal_stop(uv_signal_t* handle);
|
||||
static void uv__signal_unregister_handler(int signum);
|
||||
|
||||
|
||||
static uv_once_t uv__signal_global_init_guard = UV_ONCE_INIT;
|
||||
static struct uv__signal_tree_s uv__signal_tree =
|
||||
RB_INITIALIZER(uv__signal_tree);
|
||||
static int uv__signal_lock_pipefd[2];
|
||||
|
||||
|
||||
RB_GENERATE_STATIC(uv__signal_tree_s,
|
||||
uv_signal_s, tree_entry,
|
||||
uv__signal_compare)
|
||||
|
||||
static void uv__signal_global_reinit(void);
|
||||
|
||||
static void uv__signal_global_init(void) {
|
||||
if (!uv__signal_lock_pipefd[0])
|
||||
/* pthread_atfork can register before and after handlers, one
|
||||
* for each child. This only registers one for the child. That
|
||||
* state is both persistent and cumulative, so if we keep doing
|
||||
* it the handler functions will be called multiple times. Thus
|
||||
* we only want to do it once.
|
||||
*/
|
||||
if (pthread_atfork(NULL, NULL, &uv__signal_global_reinit))
|
||||
abort();
|
||||
|
||||
if (uv__make_pipe(uv__signal_lock_pipefd, 0))
|
||||
abort();
|
||||
|
||||
if (uv__signal_unlock())
|
||||
abort();
|
||||
}
|
||||
|
||||
|
||||
static void uv__signal_global_reinit(void) {
|
||||
/* We can only use signal-safe functions here.
|
||||
* That includes read/write and close, fortunately.
|
||||
* We do all of this directly here instead of resetting
|
||||
* uv__signal_global_init_guard because
|
||||
* uv__signal_global_once_init is only called from uv_loop_init
|
||||
* and this needs to function in existing loops.
|
||||
*/
|
||||
uv__close(uv__signal_lock_pipefd[0]);
|
||||
uv__signal_lock_pipefd[0] = -1;
|
||||
uv__close(uv__signal_lock_pipefd[1]);
|
||||
uv__signal_lock_pipefd[1] = -1;
|
||||
uv__signal_global_init();
|
||||
}
|
||||
|
||||
|
||||
void uv__signal_global_once_init(void) {
|
||||
uv_once(&uv__signal_global_init_guard, uv__signal_global_init);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int uv__signal_lock(void) {
|
||||
int r;
|
||||
char data;
|
||||
|
||||
do {
|
||||
r = read(uv__signal_lock_pipefd[0], &data, sizeof data);
|
||||
} while (r < 0 && errno == EINTR);
|
||||
|
||||
return (r < 0) ? -1 : 0;
|
||||
}
|
||||
|
||||
|
||||
static int uv__signal_unlock(void) {
|
||||
int r;
|
||||
char data = 42;
|
||||
|
||||
do {
|
||||
r = write(uv__signal_lock_pipefd[1], &data, sizeof data);
|
||||
} while (r < 0 && errno == EINTR);
|
||||
|
||||
return (r < 0) ? -1 : 0;
|
||||
}
|
||||
|
||||
|
||||
static void uv__signal_block_and_lock(sigset_t* saved_sigmask) {
|
||||
sigset_t new_mask;
|
||||
|
||||
if (sigfillset(&new_mask))
|
||||
abort();
|
||||
|
||||
if (pthread_sigmask(SIG_SETMASK, &new_mask, saved_sigmask))
|
||||
abort();
|
||||
|
||||
if (uv__signal_lock())
|
||||
abort();
|
||||
}
|
||||
|
||||
|
||||
static void uv__signal_unlock_and_unblock(sigset_t* saved_sigmask) {
|
||||
if (uv__signal_unlock())
|
||||
abort();
|
||||
|
||||
if (pthread_sigmask(SIG_SETMASK, saved_sigmask, NULL))
|
||||
abort();
|
||||
}
|
||||
|
||||
|
||||
static uv_signal_t* uv__signal_first_handle(int signum) {
|
||||
/* This function must be called with the signal lock held. */
|
||||
uv_signal_t lookup;
|
||||
uv_signal_t* handle;
|
||||
|
||||
lookup.signum = signum;
|
||||
lookup.flags = 0;
|
||||
lookup.loop = NULL;
|
||||
|
||||
handle = RB_NFIND(uv__signal_tree_s, &uv__signal_tree, &lookup);
|
||||
|
||||
if (handle != NULL && handle->signum == signum)
|
||||
return handle;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static void uv__signal_handler(int signum) {
|
||||
uv__signal_msg_t msg;
|
||||
uv_signal_t* handle;
|
||||
int saved_errno;
|
||||
|
||||
saved_errno = errno;
|
||||
memset(&msg, 0, sizeof msg);
|
||||
|
||||
if (uv__signal_lock()) {
|
||||
errno = saved_errno;
|
||||
return;
|
||||
}
|
||||
|
||||
for (handle = uv__signal_first_handle(signum);
|
||||
handle != NULL && handle->signum == signum;
|
||||
handle = RB_NEXT(uv__signal_tree_s, &uv__signal_tree, handle)) {
|
||||
int r;
|
||||
|
||||
msg.signum = signum;
|
||||
msg.handle = handle;
|
||||
|
||||
/* write() should be atomic for small data chunks, so the entire message
|
||||
* should be written at once. In theory the pipe could become full, in
|
||||
* which case the user is out of luck.
|
||||
*/
|
||||
do {
|
||||
r = write(handle->loop->signal_pipefd[1], &msg, sizeof msg);
|
||||
} while (r == -1 && errno == EINTR);
|
||||
|
||||
assert(r == sizeof msg ||
|
||||
(r == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)));
|
||||
|
||||
if (r != -1)
|
||||
handle->caught_signals++;
|
||||
}
|
||||
|
||||
uv__signal_unlock();
|
||||
errno = saved_errno;
|
||||
}
|
||||
|
||||
|
||||
static int uv__signal_register_handler(int signum, int oneshot) {
|
||||
/* When this function is called, the signal lock must be held. */
|
||||
struct sigaction sa;
|
||||
|
||||
/* XXX use a separate signal stack? */
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
if (sigfillset(&sa.sa_mask))
|
||||
abort();
|
||||
sa.sa_handler = uv__signal_handler;
|
||||
sa.sa_flags = oneshot ? SA_RESETHAND : 0;
|
||||
|
||||
/* XXX save old action so we can restore it later on? */
|
||||
if (sigaction(signum, &sa, NULL))
|
||||
return -errno;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void uv__signal_unregister_handler(int signum) {
|
||||
/* When this function is called, the signal lock must be held. */
|
||||
struct sigaction sa;
|
||||
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
sa.sa_handler = SIG_DFL;
|
||||
|
||||
/* sigaction can only fail with EINVAL or EFAULT; an attempt to deregister a
|
||||
* signal implies that it was successfully registered earlier, so EINVAL
|
||||
* should never happen.
|
||||
*/
|
||||
if (sigaction(signum, &sa, NULL))
|
||||
abort();
|
||||
}
|
||||
|
||||
|
||||
static int uv__signal_loop_once_init(uv_loop_t* loop) {
|
||||
int err;
|
||||
|
||||
/* Return if already initialized. */
|
||||
if (loop->signal_pipefd[0] != -1)
|
||||
return 0;
|
||||
|
||||
err = uv__make_pipe(loop->signal_pipefd, UV__F_NONBLOCK);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
uv__io_init(&loop->signal_io_watcher,
|
||||
uv__signal_event,
|
||||
loop->signal_pipefd[0]);
|
||||
uv__io_start(loop, &loop->signal_io_watcher, POLLIN);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv__signal_loop_fork(uv_loop_t* loop) {
|
||||
uv__io_stop(loop, &loop->signal_io_watcher, POLLIN);
|
||||
uv__close(loop->signal_pipefd[0]);
|
||||
uv__close(loop->signal_pipefd[1]);
|
||||
loop->signal_pipefd[0] = -1;
|
||||
loop->signal_pipefd[1] = -1;
|
||||
return uv__signal_loop_once_init(loop);
|
||||
}
|
||||
|
||||
|
||||
void uv__signal_loop_cleanup(uv_loop_t* loop) {
|
||||
QUEUE* q;
|
||||
|
||||
/* Stop all the signal watchers that are still attached to this loop. This
|
||||
* ensures that the (shared) signal tree doesn't contain any invalid entries
|
||||
* entries, and that signal handlers are removed when appropriate.
|
||||
* It's safe to use QUEUE_FOREACH here because the handles and the handle
|
||||
* queue are not modified by uv__signal_stop().
|
||||
*/
|
||||
QUEUE_FOREACH(q, &loop->handle_queue) {
|
||||
uv_handle_t* handle = QUEUE_DATA(q, uv_handle_t, handle_queue);
|
||||
|
||||
if (handle->type == UV_SIGNAL)
|
||||
uv__signal_stop((uv_signal_t*) handle);
|
||||
}
|
||||
|
||||
if (loop->signal_pipefd[0] != -1) {
|
||||
uv__close(loop->signal_pipefd[0]);
|
||||
loop->signal_pipefd[0] = -1;
|
||||
}
|
||||
|
||||
if (loop->signal_pipefd[1] != -1) {
|
||||
uv__close(loop->signal_pipefd[1]);
|
||||
loop->signal_pipefd[1] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int uv_signal_init(uv_loop_t* loop, uv_signal_t* handle) {
|
||||
int err;
|
||||
|
||||
err = uv__signal_loop_once_init(loop);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
uv__handle_init(loop, (uv_handle_t*) handle, UV_SIGNAL);
|
||||
handle->signum = 0;
|
||||
handle->caught_signals = 0;
|
||||
handle->dispatched_signals = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void uv__signal_close(uv_signal_t* handle) {
|
||||
|
||||
uv__signal_stop(handle);
|
||||
|
||||
/* If there are any caught signals "trapped" in the signal pipe, we can't
|
||||
* call the close callback yet. Otherwise, add the handle to the finish_close
|
||||
* queue.
|
||||
*/
|
||||
if (handle->caught_signals == handle->dispatched_signals) {
|
||||
uv__make_close_pending((uv_handle_t*) handle);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int uv_signal_start(uv_signal_t* handle, uv_signal_cb signal_cb, int signum) {
|
||||
return uv__signal_start(handle, signal_cb, signum, 0);
|
||||
}
|
||||
|
||||
|
||||
int uv_signal_start_oneshot(uv_signal_t* handle,
|
||||
uv_signal_cb signal_cb,
|
||||
int signum) {
|
||||
return uv__signal_start(handle, signal_cb, signum, 1);
|
||||
}
|
||||
|
||||
|
||||
static int uv__signal_start(uv_signal_t* handle,
|
||||
uv_signal_cb signal_cb,
|
||||
int signum,
|
||||
int oneshot) {
|
||||
sigset_t saved_sigmask;
|
||||
int err;
|
||||
uv_signal_t* first_handle;
|
||||
|
||||
assert(!uv__is_closing(handle));
|
||||
|
||||
/* If the user supplies signum == 0, then return an error already. If the
|
||||
* signum is otherwise invalid then uv__signal_register will find out
|
||||
* eventually.
|
||||
*/
|
||||
if (signum == 0)
|
||||
return -EINVAL;
|
||||
|
||||
/* Short circuit: if the signal watcher is already watching {signum} don't
|
||||
* go through the process of deregistering and registering the handler.
|
||||
* Additionally, this avoids pending signals getting lost in the small time
|
||||
* time frame that handle->signum == 0.
|
||||
*/
|
||||
if (signum == handle->signum) {
|
||||
handle->signal_cb = signal_cb;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* If the signal handler was already active, stop it first. */
|
||||
if (handle->signum != 0) {
|
||||
uv__signal_stop(handle);
|
||||
}
|
||||
|
||||
uv__signal_block_and_lock(&saved_sigmask);
|
||||
|
||||
/* If at this point there are no active signal watchers for this signum (in
|
||||
* any of the loops), it's time to try and register a handler for it here.
|
||||
* Also in case there's only one-shot handlers and a regular handler comes in.
|
||||
*/
|
||||
first_handle = uv__signal_first_handle(signum);
|
||||
if (first_handle == NULL ||
|
||||
(!oneshot && (first_handle->flags & UV__SIGNAL_ONE_SHOT))) {
|
||||
err = uv__signal_register_handler(signum, oneshot);
|
||||
if (err) {
|
||||
/* Registering the signal handler failed. Must be an invalid signal. */
|
||||
uv__signal_unlock_and_unblock(&saved_sigmask);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
handle->signum = signum;
|
||||
if (oneshot)
|
||||
handle->flags |= UV__SIGNAL_ONE_SHOT;
|
||||
|
||||
RB_INSERT(uv__signal_tree_s, &uv__signal_tree, handle);
|
||||
|
||||
uv__signal_unlock_and_unblock(&saved_sigmask);
|
||||
|
||||
handle->signal_cb = signal_cb;
|
||||
uv__handle_start(handle);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void uv__signal_event(uv_loop_t* loop,
|
||||
uv__io_t* w,
|
||||
unsigned int events) {
|
||||
uv__signal_msg_t* msg;
|
||||
uv_signal_t* handle;
|
||||
char buf[sizeof(uv__signal_msg_t) * 32];
|
||||
size_t bytes, end, i;
|
||||
int r;
|
||||
|
||||
bytes = 0;
|
||||
end = 0;
|
||||
|
||||
do {
|
||||
r = read(loop->signal_pipefd[0], buf + bytes, sizeof(buf) - bytes);
|
||||
|
||||
if (r == -1 && errno == EINTR)
|
||||
continue;
|
||||
|
||||
if (r == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
|
||||
/* If there are bytes in the buffer already (which really is extremely
|
||||
* unlikely if possible at all) we can't exit the function here. We'll
|
||||
* spin until more bytes are read instead.
|
||||
*/
|
||||
if (bytes > 0)
|
||||
continue;
|
||||
|
||||
/* Otherwise, there was nothing there. */
|
||||
return;
|
||||
}
|
||||
|
||||
/* Other errors really should never happen. */
|
||||
if (r == -1)
|
||||
abort();
|
||||
|
||||
bytes += r;
|
||||
|
||||
/* `end` is rounded down to a multiple of sizeof(uv__signal_msg_t). */
|
||||
end = (bytes / sizeof(uv__signal_msg_t)) * sizeof(uv__signal_msg_t);
|
||||
|
||||
for (i = 0; i < end; i += sizeof(uv__signal_msg_t)) {
|
||||
msg = (uv__signal_msg_t*) (buf + i);
|
||||
handle = msg->handle;
|
||||
|
||||
if (msg->signum == handle->signum) {
|
||||
assert(!(handle->flags & UV_CLOSING));
|
||||
handle->signal_cb(handle, handle->signum);
|
||||
}
|
||||
|
||||
handle->dispatched_signals++;
|
||||
|
||||
if (handle->flags & UV__SIGNAL_ONE_SHOT)
|
||||
uv__signal_stop(handle);
|
||||
|
||||
/* If uv_close was called while there were caught signals that were not
|
||||
* yet dispatched, the uv__finish_close was deferred. Make close pending
|
||||
* now if this has happened.
|
||||
*/
|
||||
if ((handle->flags & UV_CLOSING) &&
|
||||
(handle->caught_signals == handle->dispatched_signals)) {
|
||||
uv__make_close_pending((uv_handle_t*) handle);
|
||||
}
|
||||
}
|
||||
|
||||
bytes -= end;
|
||||
|
||||
/* If there are any "partial" messages left, move them to the start of the
|
||||
* the buffer, and spin. This should not happen.
|
||||
*/
|
||||
if (bytes) {
|
||||
memmove(buf, buf + end, bytes);
|
||||
continue;
|
||||
}
|
||||
} while (end == sizeof buf);
|
||||
}
|
||||
|
||||
|
||||
static int uv__signal_compare(uv_signal_t* w1, uv_signal_t* w2) {
|
||||
int f1;
|
||||
int f2;
|
||||
/* Compare signums first so all watchers with the same signnum end up
|
||||
* adjacent.
|
||||
*/
|
||||
if (w1->signum < w2->signum) return -1;
|
||||
if (w1->signum > w2->signum) return 1;
|
||||
|
||||
/* Handlers without UV__SIGNAL_ONE_SHOT set will come first, so if the first
|
||||
* handler returned is a one-shot handler, the rest will be too.
|
||||
*/
|
||||
f1 = w1->flags & UV__SIGNAL_ONE_SHOT;
|
||||
f2 = w2->flags & UV__SIGNAL_ONE_SHOT;
|
||||
if (f1 < f2) return -1;
|
||||
if (f1 > f2) return 1;
|
||||
|
||||
/* Sort by loop pointer, so we can easily look up the first item after
|
||||
* { .signum = x, .loop = NULL }.
|
||||
*/
|
||||
if (w1->loop < w2->loop) return -1;
|
||||
if (w1->loop > w2->loop) return 1;
|
||||
|
||||
if (w1 < w2) return -1;
|
||||
if (w1 > w2) return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv_signal_stop(uv_signal_t* handle) {
|
||||
assert(!uv__is_closing(handle));
|
||||
uv__signal_stop(handle);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void uv__signal_stop(uv_signal_t* handle) {
|
||||
uv_signal_t* removed_handle;
|
||||
sigset_t saved_sigmask;
|
||||
uv_signal_t* first_handle;
|
||||
int rem_oneshot;
|
||||
int first_oneshot;
|
||||
int ret;
|
||||
|
||||
/* If the watcher wasn't started, this is a no-op. */
|
||||
if (handle->signum == 0)
|
||||
return;
|
||||
|
||||
uv__signal_block_and_lock(&saved_sigmask);
|
||||
|
||||
removed_handle = RB_REMOVE(uv__signal_tree_s, &uv__signal_tree, handle);
|
||||
assert(removed_handle == handle);
|
||||
(void) removed_handle;
|
||||
|
||||
/* Check if there are other active signal watchers observing this signal. If
|
||||
* not, unregister the signal handler.
|
||||
*/
|
||||
first_handle = uv__signal_first_handle(handle->signum);
|
||||
if (first_handle == NULL) {
|
||||
uv__signal_unregister_handler(handle->signum);
|
||||
} else {
|
||||
rem_oneshot = handle->flags & UV__SIGNAL_ONE_SHOT;
|
||||
first_oneshot = first_handle->flags & UV__SIGNAL_ONE_SHOT;
|
||||
if (first_oneshot && !rem_oneshot) {
|
||||
ret = uv__signal_register_handler(handle->signum, 1);
|
||||
assert(ret == 0);
|
||||
}
|
||||
}
|
||||
|
||||
uv__signal_unlock_and_unblock(&saved_sigmask);
|
||||
|
||||
handle->signum = 0;
|
||||
uv__handle_stop(handle);
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/* Copyright (c) 2013, Ben Noordhuis <info@bnoordhuis.nl>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef UV_SPINLOCK_H_
|
||||
#define UV_SPINLOCK_H_
|
||||
|
||||
#include "internal.h" /* ACCESS_ONCE, UV_UNUSED */
|
||||
#include "atomic-ops.h"
|
||||
|
||||
#define UV_SPINLOCK_INITIALIZER { 0 }
|
||||
|
||||
typedef struct {
|
||||
int lock;
|
||||
} uv_spinlock_t;
|
||||
|
||||
UV_UNUSED(static void uv_spinlock_init(uv_spinlock_t* spinlock));
|
||||
UV_UNUSED(static void uv_spinlock_lock(uv_spinlock_t* spinlock));
|
||||
UV_UNUSED(static void uv_spinlock_unlock(uv_spinlock_t* spinlock));
|
||||
UV_UNUSED(static int uv_spinlock_trylock(uv_spinlock_t* spinlock));
|
||||
|
||||
UV_UNUSED(static void uv_spinlock_init(uv_spinlock_t* spinlock)) {
|
||||
ACCESS_ONCE(int, spinlock->lock) = 0;
|
||||
}
|
||||
|
||||
UV_UNUSED(static void uv_spinlock_lock(uv_spinlock_t* spinlock)) {
|
||||
while (!uv_spinlock_trylock(spinlock)) cpu_relax();
|
||||
}
|
||||
|
||||
UV_UNUSED(static void uv_spinlock_unlock(uv_spinlock_t* spinlock)) {
|
||||
ACCESS_ONCE(int, spinlock->lock) = 0;
|
||||
}
|
||||
|
||||
UV_UNUSED(static int uv_spinlock_trylock(uv_spinlock_t* spinlock)) {
|
||||
/* TODO(bnoordhuis) Maybe change to a ticket lock to guarantee fair queueing.
|
||||
* Not really critical until we have locks that are (frequently) contended
|
||||
* for by several threads.
|
||||
*/
|
||||
return 0 == cmpxchgi(&spinlock->lock, 0, 1);
|
||||
}
|
||||
|
||||
#endif /* UV_SPINLOCK_H_ */
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,821 @@
|
||||
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "uv.h"
|
||||
#include "internal.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
|
||||
#ifndef SUNOS_NO_IFADDRS
|
||||
# include <ifaddrs.h>
|
||||
#endif
|
||||
#include <net/if.h>
|
||||
#include <net/if_dl.h>
|
||||
#include <net/if_arp.h>
|
||||
#include <sys/sockio.h>
|
||||
|
||||
#include <sys/loadavg.h>
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
#include <kstat.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <sys/port.h>
|
||||
#include <port.h>
|
||||
|
||||
#define PORT_FIRED 0x69
|
||||
#define PORT_UNUSED 0x0
|
||||
#define PORT_LOADED 0x99
|
||||
#define PORT_DELETED -1
|
||||
|
||||
#if (!defined(_LP64)) && (_FILE_OFFSET_BITS - 0 == 64)
|
||||
#define PROCFS_FILE_OFFSET_BITS_HACK 1
|
||||
#undef _FILE_OFFSET_BITS
|
||||
#else
|
||||
#define PROCFS_FILE_OFFSET_BITS_HACK 0
|
||||
#endif
|
||||
|
||||
#include <procfs.h>
|
||||
|
||||
#if (PROCFS_FILE_OFFSET_BITS_HACK - 0 == 1)
|
||||
#define _FILE_OFFSET_BITS 64
|
||||
#endif
|
||||
|
||||
|
||||
int uv__platform_loop_init(uv_loop_t* loop) {
|
||||
int err;
|
||||
int fd;
|
||||
|
||||
loop->fs_fd = -1;
|
||||
loop->backend_fd = -1;
|
||||
|
||||
fd = port_create();
|
||||
if (fd == -1)
|
||||
return -errno;
|
||||
|
||||
err = uv__cloexec(fd, 1);
|
||||
if (err) {
|
||||
uv__close(fd);
|
||||
return err;
|
||||
}
|
||||
loop->backend_fd = fd;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void uv__platform_loop_delete(uv_loop_t* loop) {
|
||||
if (loop->fs_fd != -1) {
|
||||
uv__close(loop->fs_fd);
|
||||
loop->fs_fd = -1;
|
||||
}
|
||||
|
||||
if (loop->backend_fd != -1) {
|
||||
uv__close(loop->backend_fd);
|
||||
loop->backend_fd = -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int uv__io_fork(uv_loop_t* loop) {
|
||||
#if defined(PORT_SOURCE_FILE)
|
||||
if (loop->fs_fd != -1) {
|
||||
/* stop the watcher before we blow away its fileno */
|
||||
uv__io_stop(loop, &loop->fs_event_watcher, POLLIN);
|
||||
}
|
||||
#endif
|
||||
uv__platform_loop_delete(loop);
|
||||
return uv__platform_loop_init(loop);
|
||||
}
|
||||
|
||||
|
||||
void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) {
|
||||
struct port_event* events;
|
||||
uintptr_t i;
|
||||
uintptr_t nfds;
|
||||
|
||||
assert(loop->watchers != NULL);
|
||||
|
||||
events = (struct port_event*) loop->watchers[loop->nwatchers];
|
||||
nfds = (uintptr_t) loop->watchers[loop->nwatchers + 1];
|
||||
if (events == NULL)
|
||||
return;
|
||||
|
||||
/* Invalidate events with same file descriptor */
|
||||
for (i = 0; i < nfds; i++)
|
||||
if ((int) events[i].portev_object == fd)
|
||||
events[i].portev_object = -1;
|
||||
}
|
||||
|
||||
|
||||
int uv__io_check_fd(uv_loop_t* loop, int fd) {
|
||||
if (port_associate(loop->backend_fd, PORT_SOURCE_FD, fd, POLLIN, 0))
|
||||
return -errno;
|
||||
|
||||
if (port_dissociate(loop->backend_fd, PORT_SOURCE_FD, fd))
|
||||
abort();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void uv__io_poll(uv_loop_t* loop, int timeout) {
|
||||
struct port_event events[1024];
|
||||
struct port_event* pe;
|
||||
struct timespec spec;
|
||||
QUEUE* q;
|
||||
uv__io_t* w;
|
||||
sigset_t* pset;
|
||||
sigset_t set;
|
||||
uint64_t base;
|
||||
uint64_t diff;
|
||||
unsigned int nfds;
|
||||
unsigned int i;
|
||||
int saved_errno;
|
||||
int have_signals;
|
||||
int nevents;
|
||||
int count;
|
||||
int err;
|
||||
int fd;
|
||||
|
||||
if (loop->nfds == 0) {
|
||||
assert(QUEUE_EMPTY(&loop->watcher_queue));
|
||||
return;
|
||||
}
|
||||
|
||||
while (!QUEUE_EMPTY(&loop->watcher_queue)) {
|
||||
q = QUEUE_HEAD(&loop->watcher_queue);
|
||||
QUEUE_REMOVE(q);
|
||||
QUEUE_INIT(q);
|
||||
|
||||
w = QUEUE_DATA(q, uv__io_t, watcher_queue);
|
||||
assert(w->pevents != 0);
|
||||
|
||||
if (port_associate(loop->backend_fd, PORT_SOURCE_FD, w->fd, w->pevents, 0))
|
||||
abort();
|
||||
|
||||
w->events = w->pevents;
|
||||
}
|
||||
|
||||
pset = NULL;
|
||||
if (loop->flags & UV_LOOP_BLOCK_SIGPROF) {
|
||||
pset = &set;
|
||||
sigemptyset(pset);
|
||||
sigaddset(pset, SIGPROF);
|
||||
}
|
||||
|
||||
assert(timeout >= -1);
|
||||
base = loop->time;
|
||||
count = 48; /* Benchmarks suggest this gives the best throughput. */
|
||||
|
||||
for (;;) {
|
||||
if (timeout != -1) {
|
||||
spec.tv_sec = timeout / 1000;
|
||||
spec.tv_nsec = (timeout % 1000) * 1000000;
|
||||
}
|
||||
|
||||
/* Work around a kernel bug where nfds is not updated. */
|
||||
events[0].portev_source = 0;
|
||||
|
||||
nfds = 1;
|
||||
saved_errno = 0;
|
||||
|
||||
if (pset != NULL)
|
||||
pthread_sigmask(SIG_BLOCK, pset, NULL);
|
||||
|
||||
err = port_getn(loop->backend_fd,
|
||||
events,
|
||||
ARRAY_SIZE(events),
|
||||
&nfds,
|
||||
timeout == -1 ? NULL : &spec);
|
||||
|
||||
if (pset != NULL)
|
||||
pthread_sigmask(SIG_UNBLOCK, pset, NULL);
|
||||
|
||||
if (err) {
|
||||
/* Work around another kernel bug: port_getn() may return events even
|
||||
* on error.
|
||||
*/
|
||||
if (errno == EINTR || errno == ETIME)
|
||||
saved_errno = errno;
|
||||
else
|
||||
abort();
|
||||
}
|
||||
|
||||
/* Update loop->time unconditionally. It's tempting to skip the update when
|
||||
* timeout == 0 (i.e. non-blocking poll) but there is no guarantee that the
|
||||
* operating system didn't reschedule our process while in the syscall.
|
||||
*/
|
||||
SAVE_ERRNO(uv__update_time(loop));
|
||||
|
||||
if (events[0].portev_source == 0) {
|
||||
if (timeout == 0)
|
||||
return;
|
||||
|
||||
if (timeout == -1)
|
||||
continue;
|
||||
|
||||
goto update_timeout;
|
||||
}
|
||||
|
||||
if (nfds == 0) {
|
||||
assert(timeout != -1);
|
||||
return;
|
||||
}
|
||||
|
||||
have_signals = 0;
|
||||
nevents = 0;
|
||||
|
||||
assert(loop->watchers != NULL);
|
||||
loop->watchers[loop->nwatchers] = (void*) events;
|
||||
loop->watchers[loop->nwatchers + 1] = (void*) (uintptr_t) nfds;
|
||||
for (i = 0; i < nfds; i++) {
|
||||
pe = events + i;
|
||||
fd = pe->portev_object;
|
||||
|
||||
/* Skip invalidated events, see uv__platform_invalidate_fd */
|
||||
if (fd == -1)
|
||||
continue;
|
||||
|
||||
assert(fd >= 0);
|
||||
assert((unsigned) fd < loop->nwatchers);
|
||||
|
||||
w = loop->watchers[fd];
|
||||
|
||||
/* File descriptor that we've stopped watching, ignore. */
|
||||
if (w == NULL)
|
||||
continue;
|
||||
|
||||
/* Run signal watchers last. This also affects child process watchers
|
||||
* because those are implemented in terms of signal watchers.
|
||||
*/
|
||||
if (w == &loop->signal_io_watcher)
|
||||
have_signals = 1;
|
||||
else
|
||||
w->cb(loop, w, pe->portev_events);
|
||||
|
||||
nevents++;
|
||||
|
||||
if (w != loop->watchers[fd])
|
||||
continue; /* Disabled by callback. */
|
||||
|
||||
/* Events Ports operates in oneshot mode, rearm timer on next run. */
|
||||
if (w->pevents != 0 && QUEUE_EMPTY(&w->watcher_queue))
|
||||
QUEUE_INSERT_TAIL(&loop->watcher_queue, &w->watcher_queue);
|
||||
}
|
||||
|
||||
if (have_signals != 0)
|
||||
loop->signal_io_watcher.cb(loop, &loop->signal_io_watcher, POLLIN);
|
||||
|
||||
loop->watchers[loop->nwatchers] = NULL;
|
||||
loop->watchers[loop->nwatchers + 1] = NULL;
|
||||
|
||||
if (have_signals != 0)
|
||||
return; /* Event loop should cycle now so don't poll again. */
|
||||
|
||||
if (nevents != 0) {
|
||||
if (nfds == ARRAY_SIZE(events) && --count != 0) {
|
||||
/* Poll for more events but don't block this time. */
|
||||
timeout = 0;
|
||||
continue;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (saved_errno == ETIME) {
|
||||
assert(timeout != -1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (timeout == 0)
|
||||
return;
|
||||
|
||||
if (timeout == -1)
|
||||
continue;
|
||||
|
||||
update_timeout:
|
||||
assert(timeout > 0);
|
||||
|
||||
diff = loop->time - base;
|
||||
if (diff >= (uint64_t) timeout)
|
||||
return;
|
||||
|
||||
timeout -= diff;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
uint64_t uv__hrtime(uv_clocktype_t type) {
|
||||
return gethrtime();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* We could use a static buffer for the path manipulations that we need outside
|
||||
* of the function, but this function could be called by multiple consumers and
|
||||
* we don't want to potentially create a race condition in the use of snprintf.
|
||||
*/
|
||||
int uv_exepath(char* buffer, size_t* size) {
|
||||
ssize_t res;
|
||||
char buf[128];
|
||||
|
||||
if (buffer == NULL || size == NULL || *size == 0)
|
||||
return -EINVAL;
|
||||
|
||||
snprintf(buf, sizeof(buf), "/proc/%lu/path/a.out", (unsigned long) getpid());
|
||||
|
||||
res = *size - 1;
|
||||
if (res > 0)
|
||||
res = readlink(buf, buffer, res);
|
||||
|
||||
if (res == -1)
|
||||
return -errno;
|
||||
|
||||
buffer[res] = '\0';
|
||||
*size = res;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
uint64_t uv_get_free_memory(void) {
|
||||
return (uint64_t) sysconf(_SC_PAGESIZE) * sysconf(_SC_AVPHYS_PAGES);
|
||||
}
|
||||
|
||||
|
||||
uint64_t uv_get_total_memory(void) {
|
||||
return (uint64_t) sysconf(_SC_PAGESIZE) * sysconf(_SC_PHYS_PAGES);
|
||||
}
|
||||
|
||||
|
||||
void uv_loadavg(double avg[3]) {
|
||||
(void) getloadavg(avg, 3);
|
||||
}
|
||||
|
||||
|
||||
#if defined(PORT_SOURCE_FILE)
|
||||
|
||||
static int uv__fs_event_rearm(uv_fs_event_t *handle) {
|
||||
if (handle->fd == -1)
|
||||
return -EBADF;
|
||||
|
||||
if (port_associate(handle->loop->fs_fd,
|
||||
PORT_SOURCE_FILE,
|
||||
(uintptr_t) &handle->fo,
|
||||
FILE_ATTRIB | FILE_MODIFIED,
|
||||
handle) == -1) {
|
||||
return -errno;
|
||||
}
|
||||
handle->fd = PORT_LOADED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void uv__fs_event_read(uv_loop_t* loop,
|
||||
uv__io_t* w,
|
||||
unsigned int revents) {
|
||||
uv_fs_event_t *handle = NULL;
|
||||
timespec_t timeout;
|
||||
port_event_t pe;
|
||||
int events;
|
||||
int r;
|
||||
|
||||
(void) w;
|
||||
(void) revents;
|
||||
|
||||
do {
|
||||
uint_t n = 1;
|
||||
|
||||
/*
|
||||
* Note that our use of port_getn() here (and not port_get()) is deliberate:
|
||||
* there is a bug in event ports (Sun bug 6456558) whereby a zeroed timeout
|
||||
* causes port_get() to return success instead of ETIME when there aren't
|
||||
* actually any events (!); by using port_getn() in lieu of port_get(),
|
||||
* we can at least workaround the bug by checking for zero returned events
|
||||
* and treating it as we would ETIME.
|
||||
*/
|
||||
do {
|
||||
memset(&timeout, 0, sizeof timeout);
|
||||
r = port_getn(loop->fs_fd, &pe, 1, &n, &timeout);
|
||||
}
|
||||
while (r == -1 && errno == EINTR);
|
||||
|
||||
if ((r == -1 && errno == ETIME) || n == 0)
|
||||
break;
|
||||
|
||||
handle = (uv_fs_event_t*) pe.portev_user;
|
||||
assert((r == 0) && "unexpected port_get() error");
|
||||
|
||||
events = 0;
|
||||
if (pe.portev_events & (FILE_ATTRIB | FILE_MODIFIED))
|
||||
events |= UV_CHANGE;
|
||||
if (pe.portev_events & ~(FILE_ATTRIB | FILE_MODIFIED))
|
||||
events |= UV_RENAME;
|
||||
assert(events != 0);
|
||||
handle->fd = PORT_FIRED;
|
||||
handle->cb(handle, NULL, events, 0);
|
||||
|
||||
if (handle->fd != PORT_DELETED) {
|
||||
r = uv__fs_event_rearm(handle);
|
||||
if (r != 0)
|
||||
handle->cb(handle, NULL, 0, r);
|
||||
}
|
||||
}
|
||||
while (handle->fd != PORT_DELETED);
|
||||
}
|
||||
|
||||
|
||||
int uv_fs_event_init(uv_loop_t* loop, uv_fs_event_t* handle) {
|
||||
uv__handle_init(loop, (uv_handle_t*)handle, UV_FS_EVENT);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv_fs_event_start(uv_fs_event_t* handle,
|
||||
uv_fs_event_cb cb,
|
||||
const char* path,
|
||||
unsigned int flags) {
|
||||
int portfd;
|
||||
int first_run;
|
||||
int err;
|
||||
|
||||
if (uv__is_active(handle))
|
||||
return -EINVAL;
|
||||
|
||||
first_run = 0;
|
||||
if (handle->loop->fs_fd == -1) {
|
||||
portfd = port_create();
|
||||
if (portfd == -1)
|
||||
return -errno;
|
||||
handle->loop->fs_fd = portfd;
|
||||
first_run = 1;
|
||||
}
|
||||
|
||||
uv__handle_start(handle);
|
||||
handle->path = uv__strdup(path);
|
||||
handle->fd = PORT_UNUSED;
|
||||
handle->cb = cb;
|
||||
|
||||
memset(&handle->fo, 0, sizeof handle->fo);
|
||||
handle->fo.fo_name = handle->path;
|
||||
err = uv__fs_event_rearm(handle);
|
||||
if (err != 0) {
|
||||
uv_fs_event_stop(handle);
|
||||
return err;
|
||||
}
|
||||
|
||||
if (first_run) {
|
||||
uv__io_init(&handle->loop->fs_event_watcher, uv__fs_event_read, portfd);
|
||||
uv__io_start(handle->loop, &handle->loop->fs_event_watcher, POLLIN);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv_fs_event_stop(uv_fs_event_t* handle) {
|
||||
if (!uv__is_active(handle))
|
||||
return 0;
|
||||
|
||||
if (handle->fd == PORT_FIRED || handle->fd == PORT_LOADED) {
|
||||
port_dissociate(handle->loop->fs_fd,
|
||||
PORT_SOURCE_FILE,
|
||||
(uintptr_t) &handle->fo);
|
||||
}
|
||||
|
||||
handle->fd = PORT_DELETED;
|
||||
uv__free(handle->path);
|
||||
handle->path = NULL;
|
||||
handle->fo.fo_name = NULL;
|
||||
uv__handle_stop(handle);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void uv__fs_event_close(uv_fs_event_t* handle) {
|
||||
uv_fs_event_stop(handle);
|
||||
}
|
||||
|
||||
#else /* !defined(PORT_SOURCE_FILE) */
|
||||
|
||||
int uv_fs_event_init(uv_loop_t* loop, uv_fs_event_t* handle) {
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
|
||||
int uv_fs_event_start(uv_fs_event_t* handle,
|
||||
uv_fs_event_cb cb,
|
||||
const char* filename,
|
||||
unsigned int flags) {
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
|
||||
int uv_fs_event_stop(uv_fs_event_t* handle) {
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
|
||||
void uv__fs_event_close(uv_fs_event_t* handle) {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
#endif /* defined(PORT_SOURCE_FILE) */
|
||||
|
||||
|
||||
int uv_resident_set_memory(size_t* rss) {
|
||||
psinfo_t psinfo;
|
||||
int err;
|
||||
int fd;
|
||||
|
||||
fd = open("/proc/self/psinfo", O_RDONLY);
|
||||
if (fd == -1)
|
||||
return -errno;
|
||||
|
||||
/* FIXME(bnoordhuis) Handle EINTR. */
|
||||
err = -EINVAL;
|
||||
if (read(fd, &psinfo, sizeof(psinfo)) == sizeof(psinfo)) {
|
||||
*rss = (size_t)psinfo.pr_rssize * 1024;
|
||||
err = 0;
|
||||
}
|
||||
uv__close(fd);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
int uv_uptime(double* uptime) {
|
||||
kstat_ctl_t *kc;
|
||||
kstat_t *ksp;
|
||||
kstat_named_t *knp;
|
||||
|
||||
long hz = sysconf(_SC_CLK_TCK);
|
||||
|
||||
kc = kstat_open();
|
||||
if (kc == NULL)
|
||||
return -EPERM;
|
||||
|
||||
ksp = kstat_lookup(kc, (char*) "unix", 0, (char*) "system_misc");
|
||||
if (kstat_read(kc, ksp, NULL) == -1) {
|
||||
*uptime = -1;
|
||||
} else {
|
||||
knp = (kstat_named_t*) kstat_data_lookup(ksp, (char*) "clk_intr");
|
||||
*uptime = knp->value.ul / hz;
|
||||
}
|
||||
kstat_close(kc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) {
|
||||
int lookup_instance;
|
||||
kstat_ctl_t *kc;
|
||||
kstat_t *ksp;
|
||||
kstat_named_t *knp;
|
||||
uv_cpu_info_t* cpu_info;
|
||||
|
||||
kc = kstat_open();
|
||||
if (kc == NULL)
|
||||
return -EPERM;
|
||||
|
||||
/* Get count of cpus */
|
||||
lookup_instance = 0;
|
||||
while ((ksp = kstat_lookup(kc, (char*) "cpu_info", lookup_instance, NULL))) {
|
||||
lookup_instance++;
|
||||
}
|
||||
|
||||
*cpu_infos = uv__malloc(lookup_instance * sizeof(**cpu_infos));
|
||||
if (!(*cpu_infos)) {
|
||||
kstat_close(kc);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
*count = lookup_instance;
|
||||
|
||||
cpu_info = *cpu_infos;
|
||||
lookup_instance = 0;
|
||||
while ((ksp = kstat_lookup(kc, (char*) "cpu_info", lookup_instance, NULL))) {
|
||||
if (kstat_read(kc, ksp, NULL) == -1) {
|
||||
cpu_info->speed = 0;
|
||||
cpu_info->model = NULL;
|
||||
} else {
|
||||
knp = kstat_data_lookup(ksp, (char*) "clock_MHz");
|
||||
assert(knp->data_type == KSTAT_DATA_INT32 ||
|
||||
knp->data_type == KSTAT_DATA_INT64);
|
||||
cpu_info->speed = (knp->data_type == KSTAT_DATA_INT32) ? knp->value.i32
|
||||
: knp->value.i64;
|
||||
|
||||
knp = kstat_data_lookup(ksp, (char*) "brand");
|
||||
assert(knp->data_type == KSTAT_DATA_STRING);
|
||||
cpu_info->model = uv__strdup(KSTAT_NAMED_STR_PTR(knp));
|
||||
}
|
||||
|
||||
lookup_instance++;
|
||||
cpu_info++;
|
||||
}
|
||||
|
||||
cpu_info = *cpu_infos;
|
||||
lookup_instance = 0;
|
||||
for (;;) {
|
||||
ksp = kstat_lookup(kc, (char*) "cpu", lookup_instance, (char*) "sys");
|
||||
|
||||
if (ksp == NULL)
|
||||
break;
|
||||
|
||||
if (kstat_read(kc, ksp, NULL) == -1) {
|
||||
cpu_info->cpu_times.user = 0;
|
||||
cpu_info->cpu_times.nice = 0;
|
||||
cpu_info->cpu_times.sys = 0;
|
||||
cpu_info->cpu_times.idle = 0;
|
||||
cpu_info->cpu_times.irq = 0;
|
||||
} else {
|
||||
knp = kstat_data_lookup(ksp, (char*) "cpu_ticks_user");
|
||||
assert(knp->data_type == KSTAT_DATA_UINT64);
|
||||
cpu_info->cpu_times.user = knp->value.ui64;
|
||||
|
||||
knp = kstat_data_lookup(ksp, (char*) "cpu_ticks_kernel");
|
||||
assert(knp->data_type == KSTAT_DATA_UINT64);
|
||||
cpu_info->cpu_times.sys = knp->value.ui64;
|
||||
|
||||
knp = kstat_data_lookup(ksp, (char*) "cpu_ticks_idle");
|
||||
assert(knp->data_type == KSTAT_DATA_UINT64);
|
||||
cpu_info->cpu_times.idle = knp->value.ui64;
|
||||
|
||||
knp = kstat_data_lookup(ksp, (char*) "intr");
|
||||
assert(knp->data_type == KSTAT_DATA_UINT64);
|
||||
cpu_info->cpu_times.irq = knp->value.ui64;
|
||||
cpu_info->cpu_times.nice = 0;
|
||||
}
|
||||
|
||||
lookup_instance++;
|
||||
cpu_info++;
|
||||
}
|
||||
|
||||
kstat_close(kc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
uv__free(cpu_infos[i].model);
|
||||
}
|
||||
|
||||
uv__free(cpu_infos);
|
||||
}
|
||||
|
||||
#ifdef SUNOS_NO_IFADDRS
|
||||
int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
|
||||
return -ENOSYS;
|
||||
}
|
||||
#else /* SUNOS_NO_IFADDRS */
|
||||
/*
|
||||
* Inspired By:
|
||||
* https://blogs.oracle.com/paulie/entry/retrieving_mac_address_in_solaris
|
||||
* http://www.pauliesworld.org/project/getmac.c
|
||||
*/
|
||||
static int uv__set_phys_addr(uv_interface_address_t* address,
|
||||
struct ifaddrs* ent) {
|
||||
|
||||
struct sockaddr_dl* sa_addr;
|
||||
int sockfd;
|
||||
int i;
|
||||
struct arpreq arpreq;
|
||||
|
||||
/* This appears to only work as root */
|
||||
sa_addr = (struct sockaddr_dl*)(ent->ifa_addr);
|
||||
memcpy(address->phys_addr, LLADDR(sa_addr), sizeof(address->phys_addr));
|
||||
for (i = 0; i < sizeof(address->phys_addr); i++) {
|
||||
if (address->phys_addr[i] != 0)
|
||||
return 0;
|
||||
}
|
||||
memset(&arpreq, 0, sizeof(arpreq));
|
||||
if (address->address.address4.sin_family == AF_INET) {
|
||||
struct sockaddr_in* sin = ((struct sockaddr_in*)&arpreq.arp_pa);
|
||||
sin->sin_addr.s_addr = address->address.address4.sin_addr.s_addr;
|
||||
} else if (address->address.address4.sin_family == AF_INET6) {
|
||||
struct sockaddr_in6* sin = ((struct sockaddr_in6*)&arpreq.arp_pa);
|
||||
memcpy(sin->sin6_addr.s6_addr,
|
||||
address->address.address6.sin6_addr.s6_addr,
|
||||
sizeof(address->address.address6.sin6_addr.s6_addr));
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
|
||||
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (sockfd < 0)
|
||||
return -errno;
|
||||
|
||||
if (ioctl(sockfd, SIOCGARP, (char*)&arpreq) == -1) {
|
||||
uv__close(sockfd);
|
||||
return -errno;
|
||||
}
|
||||
memcpy(address->phys_addr, arpreq.arp_ha.sa_data, sizeof(address->phys_addr));
|
||||
uv__close(sockfd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int uv__ifaddr_exclude(struct ifaddrs *ent) {
|
||||
if (!((ent->ifa_flags & IFF_UP) && (ent->ifa_flags & IFF_RUNNING)))
|
||||
return 1;
|
||||
if (ent->ifa_addr == NULL)
|
||||
return 1;
|
||||
if (ent->ifa_addr->sa_family == PF_PACKET)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
|
||||
uv_interface_address_t* address;
|
||||
struct ifaddrs* addrs;
|
||||
struct ifaddrs* ent;
|
||||
int i;
|
||||
|
||||
if (getifaddrs(&addrs))
|
||||
return -errno;
|
||||
|
||||
*count = 0;
|
||||
|
||||
/* Count the number of interfaces */
|
||||
for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
|
||||
if (uv__ifaddr_exclude(ent))
|
||||
continue;
|
||||
(*count)++;
|
||||
}
|
||||
|
||||
*addresses = uv__malloc(*count * sizeof(**addresses));
|
||||
if (!(*addresses)) {
|
||||
freeifaddrs(addrs);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
address = *addresses;
|
||||
|
||||
for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
|
||||
if (uv__ifaddr_exclude(ent))
|
||||
continue;
|
||||
|
||||
address->name = uv__strdup(ent->ifa_name);
|
||||
|
||||
if (ent->ifa_addr->sa_family == AF_INET6) {
|
||||
address->address.address6 = *((struct sockaddr_in6*) ent->ifa_addr);
|
||||
} else {
|
||||
address->address.address4 = *((struct sockaddr_in*) ent->ifa_addr);
|
||||
}
|
||||
|
||||
if (ent->ifa_netmask->sa_family == AF_INET6) {
|
||||
address->netmask.netmask6 = *((struct sockaddr_in6*) ent->ifa_netmask);
|
||||
} else {
|
||||
address->netmask.netmask4 = *((struct sockaddr_in*) ent->ifa_netmask);
|
||||
}
|
||||
|
||||
address->is_internal = !!((ent->ifa_flags & IFF_PRIVATE) ||
|
||||
(ent->ifa_flags & IFF_LOOPBACK));
|
||||
|
||||
uv__set_phys_addr(address, ent);
|
||||
address++;
|
||||
}
|
||||
|
||||
freeifaddrs(addrs);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* SUNOS_NO_IFADDRS */
|
||||
|
||||
void uv_free_interface_addresses(uv_interface_address_t* addresses,
|
||||
int count) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
uv__free(addresses[i].name);
|
||||
}
|
||||
|
||||
uv__free(addresses);
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/* Copyright libuv project contributors. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "uv.h"
|
||||
#include "internal.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/sysinfo.h>
|
||||
|
||||
void uv_loadavg(double avg[3]) {
|
||||
struct sysinfo info;
|
||||
|
||||
if (sysinfo(&info) < 0) return;
|
||||
|
||||
avg[0] = (double) info.loads[0] / 65536.0;
|
||||
avg[1] = (double) info.loads[1] / 65536.0;
|
||||
avg[2] = (double) info.loads[2] / 65536.0;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/* Copyright libuv project contributors. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "uv.h"
|
||||
#include "internal.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/sysinfo.h>
|
||||
|
||||
uint64_t uv_get_free_memory(void) {
|
||||
struct sysinfo info;
|
||||
|
||||
if (sysinfo(&info) == 0)
|
||||
return (uint64_t) info.freeram * info.mem_unit;
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t uv_get_total_memory(void) {
|
||||
struct sysinfo info;
|
||||
|
||||
if (sysinfo(&info) == 0)
|
||||
return (uint64_t) info.totalram * info.mem_unit;
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,395 @@
|
||||
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "uv.h"
|
||||
#include "internal.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
|
||||
|
||||
static int maybe_new_socket(uv_tcp_t* handle, int domain, int flags) {
|
||||
int sockfd;
|
||||
int err;
|
||||
|
||||
if (domain == AF_UNSPEC || uv__stream_fd(handle) != -1) {
|
||||
handle->flags |= flags;
|
||||
return 0;
|
||||
}
|
||||
|
||||
err = uv__socket(domain, SOCK_STREAM, 0);
|
||||
if (err < 0)
|
||||
return err;
|
||||
sockfd = err;
|
||||
|
||||
err = uv__stream_open((uv_stream_t*) handle, sockfd, flags);
|
||||
if (err) {
|
||||
uv__close(sockfd);
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv_tcp_init_ex(uv_loop_t* loop, uv_tcp_t* tcp, unsigned int flags) {
|
||||
int domain;
|
||||
|
||||
/* Use the lower 8 bits for the domain */
|
||||
domain = flags & 0xFF;
|
||||
if (domain != AF_INET && domain != AF_INET6 && domain != AF_UNSPEC)
|
||||
return -EINVAL;
|
||||
|
||||
if (flags & ~0xFF)
|
||||
return -EINVAL;
|
||||
|
||||
uv__stream_init(loop, (uv_stream_t*)tcp, UV_TCP);
|
||||
|
||||
/* If anything fails beyond this point we need to remove the handle from
|
||||
* the handle queue, since it was added by uv__handle_init in uv_stream_init.
|
||||
*/
|
||||
|
||||
if (domain != AF_UNSPEC) {
|
||||
int err = maybe_new_socket(tcp, domain, 0);
|
||||
if (err) {
|
||||
QUEUE_REMOVE(&tcp->handle_queue);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv_tcp_init(uv_loop_t* loop, uv_tcp_t* tcp) {
|
||||
return uv_tcp_init_ex(loop, tcp, AF_UNSPEC);
|
||||
}
|
||||
|
||||
|
||||
int uv__tcp_bind(uv_tcp_t* tcp,
|
||||
const struct sockaddr* addr,
|
||||
unsigned int addrlen,
|
||||
unsigned int flags) {
|
||||
int err;
|
||||
int on;
|
||||
|
||||
/* Cannot set IPv6-only mode on non-IPv6 socket. */
|
||||
if ((flags & UV_TCP_IPV6ONLY) && addr->sa_family != AF_INET6)
|
||||
return -EINVAL;
|
||||
|
||||
err = maybe_new_socket(tcp,
|
||||
addr->sa_family,
|
||||
UV_STREAM_READABLE | UV_STREAM_WRITABLE);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
on = 1;
|
||||
if (setsockopt(tcp->io_watcher.fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)))
|
||||
return -errno;
|
||||
|
||||
#ifdef IPV6_V6ONLY
|
||||
if (addr->sa_family == AF_INET6) {
|
||||
on = (flags & UV_TCP_IPV6ONLY) != 0;
|
||||
if (setsockopt(tcp->io_watcher.fd,
|
||||
IPPROTO_IPV6,
|
||||
IPV6_V6ONLY,
|
||||
&on,
|
||||
sizeof on) == -1) {
|
||||
#if defined(__MVS__)
|
||||
if (errno == EOPNOTSUPP)
|
||||
return -EINVAL;
|
||||
#endif
|
||||
return -errno;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
errno = 0;
|
||||
if (bind(tcp->io_watcher.fd, addr, addrlen) && errno != EADDRINUSE) {
|
||||
if (errno == EAFNOSUPPORT)
|
||||
/* OSX, other BSDs and SunoS fail with EAFNOSUPPORT when binding a
|
||||
* socket created with AF_INET to an AF_INET6 address or vice versa. */
|
||||
return -EINVAL;
|
||||
return -errno;
|
||||
}
|
||||
tcp->delayed_error = -errno;
|
||||
|
||||
tcp->flags |= UV_HANDLE_BOUND;
|
||||
if (addr->sa_family == AF_INET6)
|
||||
tcp->flags |= UV_HANDLE_IPV6;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv__tcp_connect(uv_connect_t* req,
|
||||
uv_tcp_t* handle,
|
||||
const struct sockaddr* addr,
|
||||
unsigned int addrlen,
|
||||
uv_connect_cb cb) {
|
||||
int err;
|
||||
int r;
|
||||
|
||||
assert(handle->type == UV_TCP);
|
||||
|
||||
if (handle->connect_req != NULL)
|
||||
return -EALREADY; /* FIXME(bnoordhuis) -EINVAL or maybe -EBUSY. */
|
||||
|
||||
err = maybe_new_socket(handle,
|
||||
addr->sa_family,
|
||||
UV_STREAM_READABLE | UV_STREAM_WRITABLE);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
handle->delayed_error = 0;
|
||||
|
||||
do {
|
||||
errno = 0;
|
||||
r = connect(uv__stream_fd(handle), addr, addrlen);
|
||||
} while (r == -1 && errno == EINTR);
|
||||
|
||||
/* We not only check the return value, but also check the errno != 0.
|
||||
* Because in rare cases connect() will return -1 but the errno
|
||||
* is 0 (for example, on Android 4.3, OnePlus phone A0001_12_150227)
|
||||
* and actually the tcp three-way handshake is completed.
|
||||
*/
|
||||
if (r == -1 && errno != 0) {
|
||||
if (errno == EINPROGRESS)
|
||||
; /* not an error */
|
||||
else if (errno == ECONNREFUSED)
|
||||
/* If we get a ECONNREFUSED wait until the next tick to report the
|
||||
* error. Solaris wants to report immediately--other unixes want to
|
||||
* wait.
|
||||
*/
|
||||
handle->delayed_error = -errno;
|
||||
else
|
||||
return -errno;
|
||||
}
|
||||
|
||||
uv__req_init(handle->loop, req, UV_CONNECT);
|
||||
req->cb = cb;
|
||||
req->handle = (uv_stream_t*) handle;
|
||||
QUEUE_INIT(&req->queue);
|
||||
handle->connect_req = req;
|
||||
|
||||
uv__io_start(handle->loop, &handle->io_watcher, POLLOUT);
|
||||
|
||||
if (handle->delayed_error)
|
||||
uv__io_feed(handle->loop, &handle->io_watcher);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv_tcp_open(uv_tcp_t* handle, uv_os_sock_t sock) {
|
||||
int err;
|
||||
|
||||
err = uv__nonblock(sock, 1);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return uv__stream_open((uv_stream_t*)handle,
|
||||
sock,
|
||||
UV_STREAM_READABLE | UV_STREAM_WRITABLE);
|
||||
}
|
||||
|
||||
|
||||
int uv_tcp_getsockname(const uv_tcp_t* handle,
|
||||
struct sockaddr* name,
|
||||
int* namelen) {
|
||||
socklen_t socklen;
|
||||
|
||||
if (handle->delayed_error)
|
||||
return handle->delayed_error;
|
||||
|
||||
if (uv__stream_fd(handle) < 0)
|
||||
return -EINVAL; /* FIXME(bnoordhuis) -EBADF */
|
||||
|
||||
/* sizeof(socklen_t) != sizeof(int) on some systems. */
|
||||
socklen = (socklen_t) *namelen;
|
||||
|
||||
if (getsockname(uv__stream_fd(handle), name, &socklen))
|
||||
return -errno;
|
||||
|
||||
*namelen = (int) socklen;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv_tcp_getpeername(const uv_tcp_t* handle,
|
||||
struct sockaddr* name,
|
||||
int* namelen) {
|
||||
socklen_t socklen;
|
||||
|
||||
if (handle->delayed_error)
|
||||
return handle->delayed_error;
|
||||
|
||||
if (uv__stream_fd(handle) < 0)
|
||||
return -EINVAL; /* FIXME(bnoordhuis) -EBADF */
|
||||
|
||||
/* sizeof(socklen_t) != sizeof(int) on some systems. */
|
||||
socklen = (socklen_t) *namelen;
|
||||
|
||||
if (getpeername(uv__stream_fd(handle), name, &socklen))
|
||||
return -errno;
|
||||
|
||||
*namelen = (int) socklen;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv_tcp_listen(uv_tcp_t* tcp, int backlog, uv_connection_cb cb) {
|
||||
static int single_accept = -1;
|
||||
int err;
|
||||
|
||||
if (tcp->delayed_error)
|
||||
return tcp->delayed_error;
|
||||
|
||||
if (single_accept == -1) {
|
||||
const char* val = getenv("UV_TCP_SINGLE_ACCEPT");
|
||||
single_accept = (val != NULL && atoi(val) != 0); /* Off by default. */
|
||||
}
|
||||
|
||||
if (single_accept)
|
||||
tcp->flags |= UV_TCP_SINGLE_ACCEPT;
|
||||
|
||||
err = maybe_new_socket(tcp, AF_INET, UV_STREAM_READABLE);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
#ifdef __MVS__
|
||||
/* on zOS the listen call does not bind automatically
|
||||
if the socket is unbound. Hence the manual binding to
|
||||
an arbitrary port is required to be done manually
|
||||
*/
|
||||
|
||||
if (!(tcp->flags & UV_HANDLE_BOUND)) {
|
||||
struct sockaddr_storage saddr;
|
||||
socklen_t slen = sizeof(saddr);
|
||||
memset(&saddr, 0, sizeof(saddr));
|
||||
|
||||
if (getsockname(tcp->io_watcher.fd, (struct sockaddr*) &saddr, &slen))
|
||||
return -errno;
|
||||
|
||||
if (bind(tcp->io_watcher.fd, (struct sockaddr*) &saddr, slen))
|
||||
return -errno;
|
||||
|
||||
tcp->flags |= UV_HANDLE_BOUND;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (listen(tcp->io_watcher.fd, backlog))
|
||||
return -errno;
|
||||
|
||||
tcp->connection_cb = cb;
|
||||
tcp->flags |= UV_HANDLE_BOUND;
|
||||
|
||||
/* Start listening for connections. */
|
||||
tcp->io_watcher.cb = uv__server_io;
|
||||
uv__io_start(tcp->loop, &tcp->io_watcher, POLLIN);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv__tcp_nodelay(int fd, int on) {
|
||||
if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)))
|
||||
return -errno;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv__tcp_keepalive(int fd, int on, unsigned int delay) {
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on)))
|
||||
return -errno;
|
||||
|
||||
#ifdef TCP_KEEPIDLE
|
||||
if (on && setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &delay, sizeof(delay)))
|
||||
return -errno;
|
||||
#endif
|
||||
|
||||
/* Solaris/SmartOS, if you don't support keep-alive,
|
||||
* then don't advertise it in your system headers...
|
||||
*/
|
||||
/* FIXME(bnoordhuis) That's possibly because sizeof(delay) should be 1. */
|
||||
#if defined(TCP_KEEPALIVE) && !defined(__sun)
|
||||
if (on && setsockopt(fd, IPPROTO_TCP, TCP_KEEPALIVE, &delay, sizeof(delay)))
|
||||
return -errno;
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv_tcp_nodelay(uv_tcp_t* handle, int on) {
|
||||
int err;
|
||||
|
||||
if (uv__stream_fd(handle) != -1) {
|
||||
err = uv__tcp_nodelay(uv__stream_fd(handle), on);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (on)
|
||||
handle->flags |= UV_TCP_NODELAY;
|
||||
else
|
||||
handle->flags &= ~UV_TCP_NODELAY;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv_tcp_keepalive(uv_tcp_t* handle, int on, unsigned int delay) {
|
||||
int err;
|
||||
|
||||
if (uv__stream_fd(handle) != -1) {
|
||||
err =uv__tcp_keepalive(uv__stream_fd(handle), on, delay);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (on)
|
||||
handle->flags |= UV_TCP_KEEPALIVE;
|
||||
else
|
||||
handle->flags &= ~UV_TCP_KEEPALIVE;
|
||||
|
||||
/* TODO Store delay if uv__stream_fd(handle) == -1 but don't want to enlarge
|
||||
* uv_tcp_t with an int that's almost never used...
|
||||
*/
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv_tcp_simultaneous_accepts(uv_tcp_t* handle, int enable) {
|
||||
if (enable)
|
||||
handle->flags &= ~UV_TCP_SINGLE_ACCEPT;
|
||||
else
|
||||
handle->flags |= UV_TCP_SINGLE_ACCEPT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void uv__tcp_close(uv_tcp_t* handle) {
|
||||
uv__stream_close((uv_stream_t*)handle);
|
||||
}
|
||||
@@ -0,0 +1,575 @@
|
||||
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "uv.h"
|
||||
#include "internal.h"
|
||||
|
||||
#include <pthread.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <sys/time.h>
|
||||
#include <sys/resource.h> /* getrlimit() */
|
||||
#include <unistd.h> /* getpagesize() */
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
#ifdef __MVS__
|
||||
#include <sys/ipc.h>
|
||||
#include <sys/sem.h>
|
||||
#endif
|
||||
|
||||
#undef NANOSEC
|
||||
#define NANOSEC ((uint64_t) 1e9)
|
||||
|
||||
|
||||
int uv_thread_create(uv_thread_t *tid, void (*entry)(void *arg), void *arg) {
|
||||
int err;
|
||||
pthread_attr_t* attr;
|
||||
#if defined(__APPLE__)
|
||||
pthread_attr_t attr_storage;
|
||||
struct rlimit lim;
|
||||
#endif
|
||||
|
||||
/* On OSX threads other than the main thread are created with a reduced stack
|
||||
* size by default, adjust it to RLIMIT_STACK.
|
||||
*/
|
||||
#if defined(__APPLE__)
|
||||
if (getrlimit(RLIMIT_STACK, &lim))
|
||||
abort();
|
||||
|
||||
attr = &attr_storage;
|
||||
if (pthread_attr_init(attr))
|
||||
abort();
|
||||
|
||||
if (lim.rlim_cur != RLIM_INFINITY) {
|
||||
/* pthread_attr_setstacksize() expects page-aligned values. */
|
||||
lim.rlim_cur -= lim.rlim_cur % (rlim_t) getpagesize();
|
||||
|
||||
if (lim.rlim_cur >= PTHREAD_STACK_MIN)
|
||||
if (pthread_attr_setstacksize(attr, lim.rlim_cur))
|
||||
abort();
|
||||
}
|
||||
#else
|
||||
attr = NULL;
|
||||
#endif
|
||||
|
||||
err = pthread_create(tid, attr, (void*(*)(void*)) entry, arg);
|
||||
|
||||
if (attr != NULL)
|
||||
pthread_attr_destroy(attr);
|
||||
|
||||
return -err;
|
||||
}
|
||||
|
||||
|
||||
uv_thread_t uv_thread_self(void) {
|
||||
return pthread_self();
|
||||
}
|
||||
|
||||
int uv_thread_join(uv_thread_t *tid) {
|
||||
return -pthread_join(*tid, NULL);
|
||||
}
|
||||
|
||||
|
||||
int uv_thread_equal(const uv_thread_t* t1, const uv_thread_t* t2) {
|
||||
return pthread_equal(*t1, *t2);
|
||||
}
|
||||
|
||||
|
||||
int uv_mutex_init(uv_mutex_t* mutex) {
|
||||
#if defined(NDEBUG) || !defined(PTHREAD_MUTEX_ERRORCHECK)
|
||||
return -pthread_mutex_init(mutex, NULL);
|
||||
#else
|
||||
pthread_mutexattr_t attr;
|
||||
int err;
|
||||
|
||||
if (pthread_mutexattr_init(&attr))
|
||||
abort();
|
||||
|
||||
if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK))
|
||||
abort();
|
||||
|
||||
err = pthread_mutex_init(mutex, &attr);
|
||||
|
||||
if (pthread_mutexattr_destroy(&attr))
|
||||
abort();
|
||||
|
||||
return -err;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void uv_mutex_destroy(uv_mutex_t* mutex) {
|
||||
if (pthread_mutex_destroy(mutex))
|
||||
abort();
|
||||
}
|
||||
|
||||
|
||||
void uv_mutex_lock(uv_mutex_t* mutex) {
|
||||
if (pthread_mutex_lock(mutex))
|
||||
abort();
|
||||
}
|
||||
|
||||
|
||||
int uv_mutex_trylock(uv_mutex_t* mutex) {
|
||||
int err;
|
||||
|
||||
err = pthread_mutex_trylock(mutex);
|
||||
if (err) {
|
||||
if (err != EBUSY && err != EAGAIN)
|
||||
abort();
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void uv_mutex_unlock(uv_mutex_t* mutex) {
|
||||
if (pthread_mutex_unlock(mutex))
|
||||
abort();
|
||||
}
|
||||
|
||||
|
||||
int uv_rwlock_init(uv_rwlock_t* rwlock) {
|
||||
return -pthread_rwlock_init(rwlock, NULL);
|
||||
}
|
||||
|
||||
|
||||
void uv_rwlock_destroy(uv_rwlock_t* rwlock) {
|
||||
if (pthread_rwlock_destroy(rwlock))
|
||||
abort();
|
||||
}
|
||||
|
||||
|
||||
void uv_rwlock_rdlock(uv_rwlock_t* rwlock) {
|
||||
if (pthread_rwlock_rdlock(rwlock))
|
||||
abort();
|
||||
}
|
||||
|
||||
|
||||
int uv_rwlock_tryrdlock(uv_rwlock_t* rwlock) {
|
||||
int err;
|
||||
|
||||
err = pthread_rwlock_tryrdlock(rwlock);
|
||||
if (err) {
|
||||
if (err != EBUSY && err != EAGAIN)
|
||||
abort();
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void uv_rwlock_rdunlock(uv_rwlock_t* rwlock) {
|
||||
if (pthread_rwlock_unlock(rwlock))
|
||||
abort();
|
||||
}
|
||||
|
||||
|
||||
void uv_rwlock_wrlock(uv_rwlock_t* rwlock) {
|
||||
if (pthread_rwlock_wrlock(rwlock))
|
||||
abort();
|
||||
}
|
||||
|
||||
|
||||
int uv_rwlock_trywrlock(uv_rwlock_t* rwlock) {
|
||||
int err;
|
||||
|
||||
err = pthread_rwlock_trywrlock(rwlock);
|
||||
if (err) {
|
||||
if (err != EBUSY && err != EAGAIN)
|
||||
abort();
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void uv_rwlock_wrunlock(uv_rwlock_t* rwlock) {
|
||||
if (pthread_rwlock_unlock(rwlock))
|
||||
abort();
|
||||
}
|
||||
|
||||
|
||||
void uv_once(uv_once_t* guard, void (*callback)(void)) {
|
||||
if (pthread_once(guard, callback))
|
||||
abort();
|
||||
}
|
||||
|
||||
#if defined(__APPLE__) && defined(__MACH__)
|
||||
|
||||
int uv_sem_init(uv_sem_t* sem, unsigned int value) {
|
||||
kern_return_t err;
|
||||
|
||||
err = semaphore_create(mach_task_self(), sem, SYNC_POLICY_FIFO, value);
|
||||
if (err == KERN_SUCCESS)
|
||||
return 0;
|
||||
if (err == KERN_INVALID_ARGUMENT)
|
||||
return -EINVAL;
|
||||
if (err == KERN_RESOURCE_SHORTAGE)
|
||||
return -ENOMEM;
|
||||
|
||||
abort();
|
||||
return -EINVAL; /* Satisfy the compiler. */
|
||||
}
|
||||
|
||||
|
||||
void uv_sem_destroy(uv_sem_t* sem) {
|
||||
if (semaphore_destroy(mach_task_self(), *sem))
|
||||
abort();
|
||||
}
|
||||
|
||||
|
||||
void uv_sem_post(uv_sem_t* sem) {
|
||||
if (semaphore_signal(*sem))
|
||||
abort();
|
||||
}
|
||||
|
||||
|
||||
void uv_sem_wait(uv_sem_t* sem) {
|
||||
int r;
|
||||
|
||||
do
|
||||
r = semaphore_wait(*sem);
|
||||
while (r == KERN_ABORTED);
|
||||
|
||||
if (r != KERN_SUCCESS)
|
||||
abort();
|
||||
}
|
||||
|
||||
|
||||
int uv_sem_trywait(uv_sem_t* sem) {
|
||||
mach_timespec_t interval;
|
||||
kern_return_t err;
|
||||
|
||||
interval.tv_sec = 0;
|
||||
interval.tv_nsec = 0;
|
||||
|
||||
err = semaphore_timedwait(*sem, interval);
|
||||
if (err == KERN_SUCCESS)
|
||||
return 0;
|
||||
if (err == KERN_OPERATION_TIMED_OUT)
|
||||
return -EAGAIN;
|
||||
|
||||
abort();
|
||||
return -EINVAL; /* Satisfy the compiler. */
|
||||
}
|
||||
|
||||
#elif defined(__MVS__)
|
||||
|
||||
int uv_sem_init(uv_sem_t* sem, unsigned int value) {
|
||||
uv_sem_t semid;
|
||||
struct sembuf buf;
|
||||
int err;
|
||||
|
||||
buf.sem_num = 0;
|
||||
buf.sem_op = value;
|
||||
buf.sem_flg = 0;
|
||||
|
||||
semid = semget(IPC_PRIVATE, 1, S_IRUSR | S_IWUSR);
|
||||
if (semid == -1)
|
||||
return -errno;
|
||||
|
||||
if (-1 == semop(semid, &buf, 1)) {
|
||||
err = errno;
|
||||
if (-1 == semctl(*sem, 0, IPC_RMID))
|
||||
abort();
|
||||
return -err;
|
||||
}
|
||||
|
||||
*sem = semid;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void uv_sem_destroy(uv_sem_t* sem) {
|
||||
if (-1 == semctl(*sem, 0, IPC_RMID))
|
||||
abort();
|
||||
}
|
||||
|
||||
void uv_sem_post(uv_sem_t* sem) {
|
||||
struct sembuf buf;
|
||||
|
||||
buf.sem_num = 0;
|
||||
buf.sem_op = 1;
|
||||
buf.sem_flg = 0;
|
||||
|
||||
if (-1 == semop(*sem, &buf, 1))
|
||||
abort();
|
||||
}
|
||||
|
||||
void uv_sem_wait(uv_sem_t* sem) {
|
||||
struct sembuf buf;
|
||||
int op_status;
|
||||
|
||||
buf.sem_num = 0;
|
||||
buf.sem_op = -1;
|
||||
buf.sem_flg = 0;
|
||||
|
||||
do
|
||||
op_status = semop(*sem, &buf, 1);
|
||||
while (op_status == -1 && errno == EINTR);
|
||||
|
||||
if (op_status)
|
||||
abort();
|
||||
}
|
||||
|
||||
int uv_sem_trywait(uv_sem_t* sem) {
|
||||
struct sembuf buf;
|
||||
int op_status;
|
||||
|
||||
buf.sem_num = 0;
|
||||
buf.sem_op = -1;
|
||||
buf.sem_flg = IPC_NOWAIT;
|
||||
|
||||
do
|
||||
op_status = semop(*sem, &buf, 1);
|
||||
while (op_status == -1 && errno == EINTR);
|
||||
|
||||
if (op_status) {
|
||||
if (errno == EAGAIN)
|
||||
return -EAGAIN;
|
||||
abort();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else /* !(defined(__APPLE__) && defined(__MACH__)) */
|
||||
|
||||
int uv_sem_init(uv_sem_t* sem, unsigned int value) {
|
||||
if (sem_init(sem, 0, value))
|
||||
return -errno;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void uv_sem_destroy(uv_sem_t* sem) {
|
||||
if (sem_destroy(sem))
|
||||
abort();
|
||||
}
|
||||
|
||||
|
||||
void uv_sem_post(uv_sem_t* sem) {
|
||||
if (sem_post(sem))
|
||||
abort();
|
||||
}
|
||||
|
||||
|
||||
void uv_sem_wait(uv_sem_t* sem) {
|
||||
int r;
|
||||
|
||||
do
|
||||
r = sem_wait(sem);
|
||||
while (r == -1 && errno == EINTR);
|
||||
|
||||
if (r)
|
||||
abort();
|
||||
}
|
||||
|
||||
|
||||
int uv_sem_trywait(uv_sem_t* sem) {
|
||||
int r;
|
||||
|
||||
do
|
||||
r = sem_trywait(sem);
|
||||
while (r == -1 && errno == EINTR);
|
||||
|
||||
if (r) {
|
||||
if (errno == EAGAIN)
|
||||
return -EAGAIN;
|
||||
abort();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* defined(__APPLE__) && defined(__MACH__) */
|
||||
|
||||
|
||||
#if defined(__APPLE__) && defined(__MACH__) || defined(__MVS__)
|
||||
|
||||
int uv_cond_init(uv_cond_t* cond) {
|
||||
return -pthread_cond_init(cond, NULL);
|
||||
}
|
||||
|
||||
#else /* !(defined(__APPLE__) && defined(__MACH__)) */
|
||||
|
||||
int uv_cond_init(uv_cond_t* cond) {
|
||||
pthread_condattr_t attr;
|
||||
int err;
|
||||
|
||||
err = pthread_condattr_init(&attr);
|
||||
if (err)
|
||||
return -err;
|
||||
|
||||
#if !(defined(__ANDROID__) && defined(HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC))
|
||||
err = pthread_condattr_setclock(&attr, CLOCK_MONOTONIC);
|
||||
if (err)
|
||||
goto error2;
|
||||
#endif
|
||||
|
||||
err = pthread_cond_init(cond, &attr);
|
||||
if (err)
|
||||
goto error2;
|
||||
|
||||
err = pthread_condattr_destroy(&attr);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
pthread_cond_destroy(cond);
|
||||
error2:
|
||||
pthread_condattr_destroy(&attr);
|
||||
return -err;
|
||||
}
|
||||
|
||||
#endif /* defined(__APPLE__) && defined(__MACH__) */
|
||||
|
||||
void uv_cond_destroy(uv_cond_t* cond) {
|
||||
#if defined(__APPLE__) && defined(__MACH__)
|
||||
/* It has been reported that destroying condition variables that have been
|
||||
* signalled but not waited on can sometimes result in application crashes.
|
||||
* See https://codereview.chromium.org/1323293005.
|
||||
*/
|
||||
pthread_mutex_t mutex;
|
||||
struct timespec ts;
|
||||
int err;
|
||||
|
||||
if (pthread_mutex_init(&mutex, NULL))
|
||||
abort();
|
||||
|
||||
if (pthread_mutex_lock(&mutex))
|
||||
abort();
|
||||
|
||||
ts.tv_sec = 0;
|
||||
ts.tv_nsec = 1;
|
||||
|
||||
err = pthread_cond_timedwait_relative_np(cond, &mutex, &ts);
|
||||
if (err != 0 && err != ETIMEDOUT)
|
||||
abort();
|
||||
|
||||
if (pthread_mutex_unlock(&mutex))
|
||||
abort();
|
||||
|
||||
if (pthread_mutex_destroy(&mutex))
|
||||
abort();
|
||||
#endif /* defined(__APPLE__) && defined(__MACH__) */
|
||||
|
||||
if (pthread_cond_destroy(cond))
|
||||
abort();
|
||||
}
|
||||
|
||||
void uv_cond_signal(uv_cond_t* cond) {
|
||||
if (pthread_cond_signal(cond))
|
||||
abort();
|
||||
}
|
||||
|
||||
void uv_cond_broadcast(uv_cond_t* cond) {
|
||||
if (pthread_cond_broadcast(cond))
|
||||
abort();
|
||||
}
|
||||
|
||||
void uv_cond_wait(uv_cond_t* cond, uv_mutex_t* mutex) {
|
||||
if (pthread_cond_wait(cond, mutex))
|
||||
abort();
|
||||
}
|
||||
|
||||
|
||||
int uv_cond_timedwait(uv_cond_t* cond, uv_mutex_t* mutex, uint64_t timeout) {
|
||||
int r;
|
||||
struct timespec ts;
|
||||
|
||||
#if defined(__APPLE__) && defined(__MACH__)
|
||||
ts.tv_sec = timeout / NANOSEC;
|
||||
ts.tv_nsec = timeout % NANOSEC;
|
||||
r = pthread_cond_timedwait_relative_np(cond, mutex, &ts);
|
||||
#else
|
||||
timeout += uv__hrtime(UV_CLOCK_PRECISE);
|
||||
ts.tv_sec = timeout / NANOSEC;
|
||||
ts.tv_nsec = timeout % NANOSEC;
|
||||
#if defined(__ANDROID__) && defined(HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC)
|
||||
/*
|
||||
* The bionic pthread implementation doesn't support CLOCK_MONOTONIC,
|
||||
* but has this alternative function instead.
|
||||
*/
|
||||
r = pthread_cond_timedwait_monotonic_np(cond, mutex, &ts);
|
||||
#else
|
||||
r = pthread_cond_timedwait(cond, mutex, &ts);
|
||||
#endif /* __ANDROID__ */
|
||||
#endif
|
||||
|
||||
|
||||
if (r == 0)
|
||||
return 0;
|
||||
|
||||
if (r == ETIMEDOUT)
|
||||
return -ETIMEDOUT;
|
||||
|
||||
abort();
|
||||
return -EINVAL; /* Satisfy the compiler. */
|
||||
}
|
||||
|
||||
|
||||
int uv_barrier_init(uv_barrier_t* barrier, unsigned int count) {
|
||||
return -pthread_barrier_init(barrier, NULL, count);
|
||||
}
|
||||
|
||||
|
||||
void uv_barrier_destroy(uv_barrier_t* barrier) {
|
||||
if (pthread_barrier_destroy(barrier))
|
||||
abort();
|
||||
}
|
||||
|
||||
|
||||
int uv_barrier_wait(uv_barrier_t* barrier) {
|
||||
int r = pthread_barrier_wait(barrier);
|
||||
if (r && r != PTHREAD_BARRIER_SERIAL_THREAD)
|
||||
abort();
|
||||
return r == PTHREAD_BARRIER_SERIAL_THREAD;
|
||||
}
|
||||
|
||||
|
||||
int uv_key_create(uv_key_t* key) {
|
||||
return -pthread_key_create(key, NULL);
|
||||
}
|
||||
|
||||
|
||||
void uv_key_delete(uv_key_t* key) {
|
||||
if (pthread_key_delete(*key))
|
||||
abort();
|
||||
}
|
||||
|
||||
|
||||
void* uv_key_get(uv_key_t* key) {
|
||||
return pthread_getspecific(*key);
|
||||
}
|
||||
|
||||
|
||||
void uv_key_set(uv_key_t* key, void* value) {
|
||||
if (pthread_setspecific(*key, value))
|
||||
abort();
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "uv.h"
|
||||
#include "internal.h"
|
||||
#include "heap-inl.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
|
||||
|
||||
static int timer_less_than(const struct heap_node* ha,
|
||||
const struct heap_node* hb) {
|
||||
const uv_timer_t* a;
|
||||
const uv_timer_t* b;
|
||||
|
||||
a = container_of(ha, uv_timer_t, heap_node);
|
||||
b = container_of(hb, uv_timer_t, heap_node);
|
||||
|
||||
if (a->timeout < b->timeout)
|
||||
return 1;
|
||||
if (b->timeout < a->timeout)
|
||||
return 0;
|
||||
|
||||
/* Compare start_id when both have the same timeout. start_id is
|
||||
* allocated with loop->timer_counter in uv_timer_start().
|
||||
*/
|
||||
if (a->start_id < b->start_id)
|
||||
return 1;
|
||||
if (b->start_id < a->start_id)
|
||||
return 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv_timer_init(uv_loop_t* loop, uv_timer_t* handle) {
|
||||
uv__handle_init(loop, (uv_handle_t*)handle, UV_TIMER);
|
||||
handle->timer_cb = NULL;
|
||||
handle->repeat = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv_timer_start(uv_timer_t* handle,
|
||||
uv_timer_cb cb,
|
||||
uint64_t timeout,
|
||||
uint64_t repeat) {
|
||||
uint64_t clamped_timeout;
|
||||
|
||||
if (cb == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
if (uv__is_active(handle))
|
||||
uv_timer_stop(handle);
|
||||
|
||||
clamped_timeout = handle->loop->time + timeout;
|
||||
if (clamped_timeout < timeout)
|
||||
clamped_timeout = (uint64_t) -1;
|
||||
|
||||
handle->timer_cb = cb;
|
||||
handle->timeout = clamped_timeout;
|
||||
handle->repeat = repeat;
|
||||
/* start_id is the second index to be compared in uv__timer_cmp() */
|
||||
handle->start_id = handle->loop->timer_counter++;
|
||||
|
||||
heap_insert((struct heap*) &handle->loop->timer_heap,
|
||||
(struct heap_node*) &handle->heap_node,
|
||||
timer_less_than);
|
||||
uv__handle_start(handle);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv_timer_stop(uv_timer_t* handle) {
|
||||
if (!uv__is_active(handle))
|
||||
return 0;
|
||||
|
||||
heap_remove((struct heap*) &handle->loop->timer_heap,
|
||||
(struct heap_node*) &handle->heap_node,
|
||||
timer_less_than);
|
||||
uv__handle_stop(handle);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv_timer_again(uv_timer_t* handle) {
|
||||
if (handle->timer_cb == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
if (handle->repeat) {
|
||||
uv_timer_stop(handle);
|
||||
uv_timer_start(handle, handle->timer_cb, handle->repeat, handle->repeat);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void uv_timer_set_repeat(uv_timer_t* handle, uint64_t repeat) {
|
||||
handle->repeat = repeat;
|
||||
}
|
||||
|
||||
|
||||
uint64_t uv_timer_get_repeat(const uv_timer_t* handle) {
|
||||
return handle->repeat;
|
||||
}
|
||||
|
||||
|
||||
int uv__next_timeout(const uv_loop_t* loop) {
|
||||
const struct heap_node* heap_node;
|
||||
const uv_timer_t* handle;
|
||||
uint64_t diff;
|
||||
|
||||
heap_node = heap_min((const struct heap*) &loop->timer_heap);
|
||||
if (heap_node == NULL)
|
||||
return -1; /* block indefinitely */
|
||||
|
||||
handle = container_of(heap_node, uv_timer_t, heap_node);
|
||||
if (handle->timeout <= loop->time)
|
||||
return 0;
|
||||
|
||||
diff = handle->timeout - loop->time;
|
||||
if (diff > INT_MAX)
|
||||
diff = INT_MAX;
|
||||
|
||||
return diff;
|
||||
}
|
||||
|
||||
|
||||
void uv__run_timers(uv_loop_t* loop) {
|
||||
struct heap_node* heap_node;
|
||||
uv_timer_t* handle;
|
||||
|
||||
for (;;) {
|
||||
heap_node = heap_min((struct heap*) &loop->timer_heap);
|
||||
if (heap_node == NULL)
|
||||
break;
|
||||
|
||||
handle = container_of(heap_node, uv_timer_t, heap_node);
|
||||
if (handle->timeout > loop->time)
|
||||
break;
|
||||
|
||||
uv_timer_stop(handle);
|
||||
uv_timer_again(handle);
|
||||
handle->timer_cb(handle);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void uv__timer_close(uv_timer_t* handle) {
|
||||
uv_timer_stop(handle);
|
||||
}
|
||||
@@ -0,0 +1,336 @@
|
||||
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "uv.h"
|
||||
#include "internal.h"
|
||||
#include "spinlock.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <unistd.h>
|
||||
#include <termios.h>
|
||||
#include <errno.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#if defined(__MVS__) && !defined(IMAXBEL)
|
||||
#define IMAXBEL 0
|
||||
#endif
|
||||
|
||||
static int orig_termios_fd = -1;
|
||||
static struct termios orig_termios;
|
||||
static uv_spinlock_t termios_spinlock = UV_SPINLOCK_INITIALIZER;
|
||||
|
||||
static int uv__tty_is_slave(const int fd) {
|
||||
int result;
|
||||
#if defined(__linux__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
|
||||
int dummy;
|
||||
|
||||
result = ioctl(fd, TIOCGPTN, &dummy) != 0;
|
||||
#elif defined(__APPLE__)
|
||||
char dummy[256];
|
||||
|
||||
result = ioctl(fd, TIOCPTYGNAME, &dummy) != 0;
|
||||
#else
|
||||
/* Fallback to ptsname
|
||||
*/
|
||||
result = ptsname(fd) == NULL;
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
int uv_tty_init(uv_loop_t* loop, uv_tty_t* tty, int fd, int readable) {
|
||||
uv_handle_type type;
|
||||
int flags;
|
||||
int newfd;
|
||||
int r;
|
||||
int saved_flags;
|
||||
char path[256];
|
||||
|
||||
/* File descriptors that refer to files cannot be monitored with epoll.
|
||||
* That restriction also applies to character devices like /dev/random
|
||||
* (but obviously not /dev/tty.)
|
||||
*/
|
||||
type = uv_guess_handle(fd);
|
||||
if (type == UV_FILE || type == UV_UNKNOWN_HANDLE)
|
||||
return -EINVAL;
|
||||
|
||||
flags = 0;
|
||||
newfd = -1;
|
||||
|
||||
/* Reopen the file descriptor when it refers to a tty. This lets us put the
|
||||
* tty in non-blocking mode without affecting other processes that share it
|
||||
* with us.
|
||||
*
|
||||
* Example: `node | cat` - if we put our fd 0 in non-blocking mode, it also
|
||||
* affects fd 1 of `cat` because both file descriptors refer to the same
|
||||
* struct file in the kernel. When we reopen our fd 0, it points to a
|
||||
* different struct file, hence changing its properties doesn't affect
|
||||
* other processes.
|
||||
*/
|
||||
if (type == UV_TTY) {
|
||||
/* Reopening a pty in master mode won't work either because the reopened
|
||||
* pty will be in slave mode (*BSD) or reopening will allocate a new
|
||||
* master/slave pair (Linux). Therefore check if the fd points to a
|
||||
* slave device.
|
||||
*/
|
||||
if (uv__tty_is_slave(fd) && ttyname_r(fd, path, sizeof(path)) == 0)
|
||||
r = uv__open_cloexec(path, O_RDWR);
|
||||
else
|
||||
r = -1;
|
||||
|
||||
if (r < 0) {
|
||||
/* fallback to using blocking writes */
|
||||
if (!readable)
|
||||
flags |= UV_STREAM_BLOCKING;
|
||||
goto skip;
|
||||
}
|
||||
|
||||
newfd = r;
|
||||
|
||||
r = uv__dup2_cloexec(newfd, fd);
|
||||
if (r < 0 && r != -EINVAL) {
|
||||
/* EINVAL means newfd == fd which could conceivably happen if another
|
||||
* thread called close(fd) between our calls to isatty() and open().
|
||||
* That's a rather unlikely event but let's handle it anyway.
|
||||
*/
|
||||
uv__close(newfd);
|
||||
return r;
|
||||
}
|
||||
|
||||
fd = newfd;
|
||||
}
|
||||
|
||||
#if defined(__APPLE__)
|
||||
/* Save the fd flags in case we need to restore them due to an error. */
|
||||
do
|
||||
saved_flags = fcntl(fd, F_GETFL);
|
||||
while (saved_flags == -1 && errno == EINTR);
|
||||
|
||||
if (saved_flags == -1) {
|
||||
if (newfd != -1)
|
||||
uv__close(newfd);
|
||||
return -errno;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Pacify the compiler. */
|
||||
(void) &saved_flags;
|
||||
|
||||
skip:
|
||||
uv__stream_init(loop, (uv_stream_t*) tty, UV_TTY);
|
||||
|
||||
/* If anything fails beyond this point we need to remove the handle from
|
||||
* the handle queue, since it was added by uv__handle_init in uv_stream_init.
|
||||
*/
|
||||
|
||||
if (!(flags & UV_STREAM_BLOCKING))
|
||||
uv__nonblock(fd, 1);
|
||||
|
||||
#if defined(__APPLE__)
|
||||
r = uv__stream_try_select((uv_stream_t*) tty, &fd);
|
||||
if (r) {
|
||||
int rc = r;
|
||||
if (newfd != -1)
|
||||
uv__close(newfd);
|
||||
QUEUE_REMOVE(&tty->handle_queue);
|
||||
do
|
||||
r = fcntl(fd, F_SETFL, saved_flags);
|
||||
while (r == -1 && errno == EINTR);
|
||||
return rc;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (readable)
|
||||
flags |= UV_STREAM_READABLE;
|
||||
else
|
||||
flags |= UV_STREAM_WRITABLE;
|
||||
|
||||
uv__stream_open((uv_stream_t*) tty, fd, flags);
|
||||
tty->mode = UV_TTY_MODE_NORMAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void uv__tty_make_raw(struct termios* tio) {
|
||||
assert(tio != NULL);
|
||||
|
||||
#if defined __sun || defined __MVS__
|
||||
/*
|
||||
* This implementation of cfmakeraw for Solaris and derivatives is taken from
|
||||
* http://www.perkin.org.uk/posts/solaris-portability-cfmakeraw.html.
|
||||
*/
|
||||
tio->c_iflag &= ~(IMAXBEL | IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR |
|
||||
IGNCR | ICRNL | IXON);
|
||||
tio->c_oflag &= ~OPOST;
|
||||
tio->c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
|
||||
tio->c_cflag &= ~(CSIZE | PARENB);
|
||||
tio->c_cflag |= CS8;
|
||||
#else
|
||||
cfmakeraw(tio);
|
||||
#endif /* #ifdef __sun */
|
||||
}
|
||||
|
||||
int uv_tty_set_mode(uv_tty_t* tty, uv_tty_mode_t mode) {
|
||||
struct termios tmp;
|
||||
int fd;
|
||||
|
||||
if (tty->mode == (int) mode)
|
||||
return 0;
|
||||
|
||||
fd = uv__stream_fd(tty);
|
||||
if (tty->mode == UV_TTY_MODE_NORMAL && mode != UV_TTY_MODE_NORMAL) {
|
||||
if (tcgetattr(fd, &tty->orig_termios))
|
||||
return -errno;
|
||||
|
||||
/* This is used for uv_tty_reset_mode() */
|
||||
uv_spinlock_lock(&termios_spinlock);
|
||||
if (orig_termios_fd == -1) {
|
||||
orig_termios = tty->orig_termios;
|
||||
orig_termios_fd = fd;
|
||||
}
|
||||
uv_spinlock_unlock(&termios_spinlock);
|
||||
}
|
||||
|
||||
tmp = tty->orig_termios;
|
||||
switch (mode) {
|
||||
case UV_TTY_MODE_NORMAL:
|
||||
break;
|
||||
case UV_TTY_MODE_RAW:
|
||||
tmp.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
|
||||
tmp.c_oflag |= (ONLCR);
|
||||
tmp.c_cflag |= (CS8);
|
||||
tmp.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
|
||||
tmp.c_cc[VMIN] = 1;
|
||||
tmp.c_cc[VTIME] = 0;
|
||||
break;
|
||||
case UV_TTY_MODE_IO:
|
||||
uv__tty_make_raw(&tmp);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Apply changes after draining */
|
||||
if (tcsetattr(fd, TCSADRAIN, &tmp))
|
||||
return -errno;
|
||||
|
||||
tty->mode = mode;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv_tty_get_winsize(uv_tty_t* tty, int* width, int* height) {
|
||||
struct winsize ws;
|
||||
int err;
|
||||
|
||||
do
|
||||
err = ioctl(uv__stream_fd(tty), TIOCGWINSZ, &ws);
|
||||
while (err == -1 && errno == EINTR);
|
||||
|
||||
if (err == -1)
|
||||
return -errno;
|
||||
|
||||
*width = ws.ws_col;
|
||||
*height = ws.ws_row;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
uv_handle_type uv_guess_handle(uv_file file) {
|
||||
struct sockaddr sa;
|
||||
struct stat s;
|
||||
socklen_t len;
|
||||
int type;
|
||||
|
||||
if (file < 0)
|
||||
return UV_UNKNOWN_HANDLE;
|
||||
|
||||
if (isatty(file))
|
||||
return UV_TTY;
|
||||
|
||||
if (fstat(file, &s))
|
||||
return UV_UNKNOWN_HANDLE;
|
||||
|
||||
if (S_ISREG(s.st_mode))
|
||||
return UV_FILE;
|
||||
|
||||
if (S_ISCHR(s.st_mode))
|
||||
return UV_FILE; /* XXX UV_NAMED_PIPE? */
|
||||
|
||||
if (S_ISFIFO(s.st_mode))
|
||||
return UV_NAMED_PIPE;
|
||||
|
||||
if (!S_ISSOCK(s.st_mode))
|
||||
return UV_UNKNOWN_HANDLE;
|
||||
|
||||
len = sizeof(type);
|
||||
if (getsockopt(file, SOL_SOCKET, SO_TYPE, &type, &len))
|
||||
return UV_UNKNOWN_HANDLE;
|
||||
|
||||
len = sizeof(sa);
|
||||
if (getsockname(file, &sa, &len))
|
||||
return UV_UNKNOWN_HANDLE;
|
||||
|
||||
if (type == SOCK_DGRAM)
|
||||
if (sa.sa_family == AF_INET || sa.sa_family == AF_INET6)
|
||||
return UV_UDP;
|
||||
|
||||
if (type == SOCK_STREAM) {
|
||||
#if defined(_AIX) || defined(__DragonFly__)
|
||||
/* on AIX/DragonFly the getsockname call returns an empty sa structure
|
||||
* for sockets of type AF_UNIX. For all other types it will
|
||||
* return a properly filled in structure.
|
||||
*/
|
||||
if (len == 0)
|
||||
return UV_NAMED_PIPE;
|
||||
#endif /* defined(_AIX) || defined(__DragonFly__) */
|
||||
|
||||
if (sa.sa_family == AF_INET || sa.sa_family == AF_INET6)
|
||||
return UV_TCP;
|
||||
if (sa.sa_family == AF_UNIX)
|
||||
return UV_NAMED_PIPE;
|
||||
}
|
||||
|
||||
return UV_UNKNOWN_HANDLE;
|
||||
}
|
||||
|
||||
|
||||
/* This function is async signal-safe, meaning that it's safe to call from
|
||||
* inside a signal handler _unless_ execution was inside uv_tty_set_mode()'s
|
||||
* critical section when the signal was raised.
|
||||
*/
|
||||
int uv_tty_reset_mode(void) {
|
||||
int saved_errno;
|
||||
int err;
|
||||
|
||||
saved_errno = errno;
|
||||
if (!uv_spinlock_trylock(&termios_spinlock))
|
||||
return -EBUSY; /* In uv_tty_set_mode(). */
|
||||
|
||||
err = 0;
|
||||
if (orig_termios_fd != -1)
|
||||
if (tcsetattr(orig_termios_fd, TCSANOW, &orig_termios))
|
||||
err = -errno;
|
||||
|
||||
uv_spinlock_unlock(&termios_spinlock);
|
||||
errno = saved_errno;
|
||||
|
||||
return err;
|
||||
}
|
||||
@@ -0,0 +1,900 @@
|
||||
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "uv.h"
|
||||
#include "internal.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#if defined(__MVS__)
|
||||
#include <xti.h>
|
||||
#endif
|
||||
|
||||
#if defined(IPV6_JOIN_GROUP) && !defined(IPV6_ADD_MEMBERSHIP)
|
||||
# define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP
|
||||
#endif
|
||||
|
||||
#if defined(IPV6_LEAVE_GROUP) && !defined(IPV6_DROP_MEMBERSHIP)
|
||||
# define IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP
|
||||
#endif
|
||||
|
||||
|
||||
static void uv__udp_run_completed(uv_udp_t* handle);
|
||||
static void uv__udp_io(uv_loop_t* loop, uv__io_t* w, unsigned int revents);
|
||||
static void uv__udp_recvmsg(uv_udp_t* handle);
|
||||
static void uv__udp_sendmsg(uv_udp_t* handle);
|
||||
static int uv__udp_maybe_deferred_bind(uv_udp_t* handle,
|
||||
int domain,
|
||||
unsigned int flags);
|
||||
|
||||
|
||||
void uv__udp_close(uv_udp_t* handle) {
|
||||
uv__io_close(handle->loop, &handle->io_watcher);
|
||||
uv__handle_stop(handle);
|
||||
|
||||
if (handle->io_watcher.fd != -1) {
|
||||
uv__close(handle->io_watcher.fd);
|
||||
handle->io_watcher.fd = -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void uv__udp_finish_close(uv_udp_t* handle) {
|
||||
uv_udp_send_t* req;
|
||||
QUEUE* q;
|
||||
|
||||
assert(!uv__io_active(&handle->io_watcher, POLLIN | POLLOUT));
|
||||
assert(handle->io_watcher.fd == -1);
|
||||
|
||||
while (!QUEUE_EMPTY(&handle->write_queue)) {
|
||||
q = QUEUE_HEAD(&handle->write_queue);
|
||||
QUEUE_REMOVE(q);
|
||||
|
||||
req = QUEUE_DATA(q, uv_udp_send_t, queue);
|
||||
req->status = -ECANCELED;
|
||||
QUEUE_INSERT_TAIL(&handle->write_completed_queue, &req->queue);
|
||||
}
|
||||
|
||||
uv__udp_run_completed(handle);
|
||||
|
||||
assert(handle->send_queue_size == 0);
|
||||
assert(handle->send_queue_count == 0);
|
||||
|
||||
/* Now tear down the handle. */
|
||||
handle->recv_cb = NULL;
|
||||
handle->alloc_cb = NULL;
|
||||
/* but _do not_ touch close_cb */
|
||||
}
|
||||
|
||||
|
||||
static void uv__udp_run_completed(uv_udp_t* handle) {
|
||||
uv_udp_send_t* req;
|
||||
QUEUE* q;
|
||||
|
||||
assert(!(handle->flags & UV_UDP_PROCESSING));
|
||||
handle->flags |= UV_UDP_PROCESSING;
|
||||
|
||||
while (!QUEUE_EMPTY(&handle->write_completed_queue)) {
|
||||
q = QUEUE_HEAD(&handle->write_completed_queue);
|
||||
QUEUE_REMOVE(q);
|
||||
|
||||
req = QUEUE_DATA(q, uv_udp_send_t, queue);
|
||||
uv__req_unregister(handle->loop, req);
|
||||
|
||||
handle->send_queue_size -= uv__count_bufs(req->bufs, req->nbufs);
|
||||
handle->send_queue_count--;
|
||||
|
||||
if (req->bufs != req->bufsml)
|
||||
uv__free(req->bufs);
|
||||
req->bufs = NULL;
|
||||
|
||||
if (req->send_cb == NULL)
|
||||
continue;
|
||||
|
||||
/* req->status >= 0 == bytes written
|
||||
* req->status < 0 == errno
|
||||
*/
|
||||
if (req->status >= 0)
|
||||
req->send_cb(req, 0);
|
||||
else
|
||||
req->send_cb(req, req->status);
|
||||
}
|
||||
|
||||
if (QUEUE_EMPTY(&handle->write_queue)) {
|
||||
/* Pending queue and completion queue empty, stop watcher. */
|
||||
uv__io_stop(handle->loop, &handle->io_watcher, POLLOUT);
|
||||
if (!uv__io_active(&handle->io_watcher, POLLIN))
|
||||
uv__handle_stop(handle);
|
||||
}
|
||||
|
||||
handle->flags &= ~UV_UDP_PROCESSING;
|
||||
}
|
||||
|
||||
|
||||
static void uv__udp_io(uv_loop_t* loop, uv__io_t* w, unsigned int revents) {
|
||||
uv_udp_t* handle;
|
||||
|
||||
handle = container_of(w, uv_udp_t, io_watcher);
|
||||
assert(handle->type == UV_UDP);
|
||||
|
||||
if (revents & POLLIN)
|
||||
uv__udp_recvmsg(handle);
|
||||
|
||||
if (revents & POLLOUT) {
|
||||
uv__udp_sendmsg(handle);
|
||||
uv__udp_run_completed(handle);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void uv__udp_recvmsg(uv_udp_t* handle) {
|
||||
struct sockaddr_storage peer;
|
||||
struct msghdr h;
|
||||
ssize_t nread;
|
||||
uv_buf_t buf;
|
||||
int flags;
|
||||
int count;
|
||||
|
||||
assert(handle->recv_cb != NULL);
|
||||
assert(handle->alloc_cb != NULL);
|
||||
|
||||
/* Prevent loop starvation when the data comes in as fast as (or faster than)
|
||||
* we can read it. XXX Need to rearm fd if we switch to edge-triggered I/O.
|
||||
*/
|
||||
count = 32;
|
||||
|
||||
memset(&h, 0, sizeof(h));
|
||||
h.msg_name = &peer;
|
||||
|
||||
do {
|
||||
buf = uv_buf_init(NULL, 0);
|
||||
handle->alloc_cb((uv_handle_t*) handle, 64 * 1024, &buf);
|
||||
if (buf.base == NULL || buf.len == 0) {
|
||||
handle->recv_cb(handle, UV_ENOBUFS, &buf, NULL, 0);
|
||||
return;
|
||||
}
|
||||
assert(buf.base != NULL);
|
||||
|
||||
h.msg_namelen = sizeof(peer);
|
||||
h.msg_iov = (void*) &buf;
|
||||
h.msg_iovlen = 1;
|
||||
|
||||
do {
|
||||
nread = recvmsg(handle->io_watcher.fd, &h, 0);
|
||||
}
|
||||
while (nread == -1 && errno == EINTR);
|
||||
|
||||
if (nread == -1) {
|
||||
if (errno == EAGAIN || errno == EWOULDBLOCK)
|
||||
handle->recv_cb(handle, 0, &buf, NULL, 0);
|
||||
else
|
||||
handle->recv_cb(handle, -errno, &buf, NULL, 0);
|
||||
}
|
||||
else {
|
||||
const struct sockaddr *addr;
|
||||
if (h.msg_namelen == 0)
|
||||
addr = NULL;
|
||||
else
|
||||
addr = (const struct sockaddr*) &peer;
|
||||
|
||||
flags = 0;
|
||||
if (h.msg_flags & MSG_TRUNC)
|
||||
flags |= UV_UDP_PARTIAL;
|
||||
|
||||
handle->recv_cb(handle, nread, &buf, addr, flags);
|
||||
}
|
||||
}
|
||||
/* recv_cb callback may decide to pause or close the handle */
|
||||
while (nread != -1
|
||||
&& count-- > 0
|
||||
&& handle->io_watcher.fd != -1
|
||||
&& handle->recv_cb != NULL);
|
||||
}
|
||||
|
||||
|
||||
static void uv__udp_sendmsg(uv_udp_t* handle) {
|
||||
uv_udp_send_t* req;
|
||||
QUEUE* q;
|
||||
struct msghdr h;
|
||||
ssize_t size;
|
||||
|
||||
while (!QUEUE_EMPTY(&handle->write_queue)) {
|
||||
q = QUEUE_HEAD(&handle->write_queue);
|
||||
assert(q != NULL);
|
||||
|
||||
req = QUEUE_DATA(q, uv_udp_send_t, queue);
|
||||
assert(req != NULL);
|
||||
|
||||
memset(&h, 0, sizeof h);
|
||||
h.msg_name = &req->addr;
|
||||
h.msg_namelen = (req->addr.ss_family == AF_INET6 ?
|
||||
sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in));
|
||||
h.msg_iov = (struct iovec*) req->bufs;
|
||||
h.msg_iovlen = req->nbufs;
|
||||
|
||||
do {
|
||||
size = sendmsg(handle->io_watcher.fd, &h, 0);
|
||||
} while (size == -1 && errno == EINTR);
|
||||
|
||||
if (size == -1 && (errno == EAGAIN || errno == EWOULDBLOCK))
|
||||
break;
|
||||
|
||||
req->status = (size == -1 ? -errno : size);
|
||||
|
||||
/* Sending a datagram is an atomic operation: either all data
|
||||
* is written or nothing is (and EMSGSIZE is raised). That is
|
||||
* why we don't handle partial writes. Just pop the request
|
||||
* off the write queue and onto the completed queue, done.
|
||||
*/
|
||||
QUEUE_REMOVE(&req->queue);
|
||||
QUEUE_INSERT_TAIL(&handle->write_completed_queue, &req->queue);
|
||||
uv__io_feed(handle->loop, &handle->io_watcher);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* On the BSDs, SO_REUSEPORT implies SO_REUSEADDR but with some additional
|
||||
* refinements for programs that use multicast.
|
||||
*
|
||||
* Linux as of 3.9 has a SO_REUSEPORT socket option but with semantics that
|
||||
* are different from the BSDs: it _shares_ the port rather than steal it
|
||||
* from the current listener. While useful, it's not something we can emulate
|
||||
* on other platforms so we don't enable it.
|
||||
*/
|
||||
static int uv__set_reuse(int fd) {
|
||||
int yes;
|
||||
|
||||
#if defined(SO_REUSEPORT) && !defined(__linux__)
|
||||
yes = 1;
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &yes, sizeof(yes)))
|
||||
return -errno;
|
||||
#else
|
||||
yes = 1;
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)))
|
||||
return -errno;
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv__udp_bind(uv_udp_t* handle,
|
||||
const struct sockaddr* addr,
|
||||
unsigned int addrlen,
|
||||
unsigned int flags) {
|
||||
int err;
|
||||
int yes;
|
||||
int fd;
|
||||
|
||||
/* Check for bad flags. */
|
||||
if (flags & ~(UV_UDP_IPV6ONLY | UV_UDP_REUSEADDR))
|
||||
return -EINVAL;
|
||||
|
||||
/* Cannot set IPv6-only mode on non-IPv6 socket. */
|
||||
if ((flags & UV_UDP_IPV6ONLY) && addr->sa_family != AF_INET6)
|
||||
return -EINVAL;
|
||||
|
||||
fd = handle->io_watcher.fd;
|
||||
if (fd == -1) {
|
||||
err = uv__socket(addr->sa_family, SOCK_DGRAM, 0);
|
||||
if (err < 0)
|
||||
return err;
|
||||
fd = err;
|
||||
handle->io_watcher.fd = fd;
|
||||
}
|
||||
|
||||
if (flags & UV_UDP_REUSEADDR) {
|
||||
err = uv__set_reuse(fd);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (flags & UV_UDP_IPV6ONLY) {
|
||||
#ifdef IPV6_V6ONLY
|
||||
yes = 1;
|
||||
if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &yes, sizeof yes) == -1) {
|
||||
err = -errno;
|
||||
return err;
|
||||
}
|
||||
#else
|
||||
err = -ENOTSUP;
|
||||
return err;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (bind(fd, addr, addrlen)) {
|
||||
err = -errno;
|
||||
if (errno == EAFNOSUPPORT)
|
||||
/* OSX, other BSDs and SunoS fail with EAFNOSUPPORT when binding a
|
||||
* socket created with AF_INET to an AF_INET6 address or vice versa. */
|
||||
err = -EINVAL;
|
||||
return err;
|
||||
}
|
||||
|
||||
if (addr->sa_family == AF_INET6)
|
||||
handle->flags |= UV_HANDLE_IPV6;
|
||||
|
||||
handle->flags |= UV_HANDLE_BOUND;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int uv__udp_maybe_deferred_bind(uv_udp_t* handle,
|
||||
int domain,
|
||||
unsigned int flags) {
|
||||
union {
|
||||
struct sockaddr_in6 in6;
|
||||
struct sockaddr_in in;
|
||||
struct sockaddr addr;
|
||||
} taddr;
|
||||
socklen_t addrlen;
|
||||
|
||||
if (handle->io_watcher.fd != -1)
|
||||
return 0;
|
||||
|
||||
switch (domain) {
|
||||
case AF_INET:
|
||||
{
|
||||
struct sockaddr_in* addr = &taddr.in;
|
||||
memset(addr, 0, sizeof *addr);
|
||||
addr->sin_family = AF_INET;
|
||||
addr->sin_addr.s_addr = INADDR_ANY;
|
||||
addrlen = sizeof *addr;
|
||||
break;
|
||||
}
|
||||
case AF_INET6:
|
||||
{
|
||||
struct sockaddr_in6* addr = &taddr.in6;
|
||||
memset(addr, 0, sizeof *addr);
|
||||
addr->sin6_family = AF_INET6;
|
||||
addr->sin6_addr = in6addr_any;
|
||||
addrlen = sizeof *addr;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
assert(0 && "unsupported address family");
|
||||
abort();
|
||||
}
|
||||
|
||||
return uv__udp_bind(handle, &taddr.addr, addrlen, flags);
|
||||
}
|
||||
|
||||
|
||||
int uv__udp_send(uv_udp_send_t* req,
|
||||
uv_udp_t* handle,
|
||||
const uv_buf_t bufs[],
|
||||
unsigned int nbufs,
|
||||
const struct sockaddr* addr,
|
||||
unsigned int addrlen,
|
||||
uv_udp_send_cb send_cb) {
|
||||
int err;
|
||||
int empty_queue;
|
||||
|
||||
assert(nbufs > 0);
|
||||
|
||||
err = uv__udp_maybe_deferred_bind(handle, addr->sa_family, 0);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* It's legal for send_queue_count > 0 even when the write_queue is empty;
|
||||
* it means there are error-state requests in the write_completed_queue that
|
||||
* will touch up send_queue_size/count later.
|
||||
*/
|
||||
empty_queue = (handle->send_queue_count == 0);
|
||||
|
||||
uv__req_init(handle->loop, req, UV_UDP_SEND);
|
||||
assert(addrlen <= sizeof(req->addr));
|
||||
memcpy(&req->addr, addr, addrlen);
|
||||
req->send_cb = send_cb;
|
||||
req->handle = handle;
|
||||
req->nbufs = nbufs;
|
||||
|
||||
req->bufs = req->bufsml;
|
||||
if (nbufs > ARRAY_SIZE(req->bufsml))
|
||||
req->bufs = uv__malloc(nbufs * sizeof(bufs[0]));
|
||||
|
||||
if (req->bufs == NULL) {
|
||||
uv__req_unregister(handle->loop, req);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
memcpy(req->bufs, bufs, nbufs * sizeof(bufs[0]));
|
||||
handle->send_queue_size += uv__count_bufs(req->bufs, req->nbufs);
|
||||
handle->send_queue_count++;
|
||||
QUEUE_INSERT_TAIL(&handle->write_queue, &req->queue);
|
||||
uv__handle_start(handle);
|
||||
|
||||
if (empty_queue && !(handle->flags & UV_UDP_PROCESSING)) {
|
||||
uv__udp_sendmsg(handle);
|
||||
|
||||
/* `uv__udp_sendmsg` may not be able to do non-blocking write straight
|
||||
* away. In such cases the `io_watcher` has to be queued for asynchronous
|
||||
* write.
|
||||
*/
|
||||
if (!QUEUE_EMPTY(&handle->write_queue))
|
||||
uv__io_start(handle->loop, &handle->io_watcher, POLLOUT);
|
||||
} else {
|
||||
uv__io_start(handle->loop, &handle->io_watcher, POLLOUT);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv__udp_try_send(uv_udp_t* handle,
|
||||
const uv_buf_t bufs[],
|
||||
unsigned int nbufs,
|
||||
const struct sockaddr* addr,
|
||||
unsigned int addrlen) {
|
||||
int err;
|
||||
struct msghdr h;
|
||||
ssize_t size;
|
||||
|
||||
assert(nbufs > 0);
|
||||
|
||||
/* already sending a message */
|
||||
if (handle->send_queue_count != 0)
|
||||
return -EAGAIN;
|
||||
|
||||
err = uv__udp_maybe_deferred_bind(handle, addr->sa_family, 0);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
memset(&h, 0, sizeof h);
|
||||
h.msg_name = (struct sockaddr*) addr;
|
||||
h.msg_namelen = addrlen;
|
||||
h.msg_iov = (struct iovec*) bufs;
|
||||
h.msg_iovlen = nbufs;
|
||||
|
||||
do {
|
||||
size = sendmsg(handle->io_watcher.fd, &h, 0);
|
||||
} while (size == -1 && errno == EINTR);
|
||||
|
||||
if (size == -1) {
|
||||
if (errno == EAGAIN || errno == EWOULDBLOCK)
|
||||
return -EAGAIN;
|
||||
else
|
||||
return -errno;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
|
||||
static int uv__udp_set_membership4(uv_udp_t* handle,
|
||||
const struct sockaddr_in* multicast_addr,
|
||||
const char* interface_addr,
|
||||
uv_membership membership) {
|
||||
struct ip_mreq mreq;
|
||||
int optname;
|
||||
int err;
|
||||
|
||||
memset(&mreq, 0, sizeof mreq);
|
||||
|
||||
if (interface_addr) {
|
||||
err = uv_inet_pton(AF_INET, interface_addr, &mreq.imr_interface.s_addr);
|
||||
if (err)
|
||||
return err;
|
||||
} else {
|
||||
mreq.imr_interface.s_addr = htonl(INADDR_ANY);
|
||||
}
|
||||
|
||||
mreq.imr_multiaddr.s_addr = multicast_addr->sin_addr.s_addr;
|
||||
|
||||
switch (membership) {
|
||||
case UV_JOIN_GROUP:
|
||||
optname = IP_ADD_MEMBERSHIP;
|
||||
break;
|
||||
case UV_LEAVE_GROUP:
|
||||
optname = IP_DROP_MEMBERSHIP;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (setsockopt(handle->io_watcher.fd,
|
||||
IPPROTO_IP,
|
||||
optname,
|
||||
&mreq,
|
||||
sizeof(mreq))) {
|
||||
#if defined(__MVS__)
|
||||
if (errno == ENXIO)
|
||||
return -ENODEV;
|
||||
#endif
|
||||
return -errno;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int uv__udp_set_membership6(uv_udp_t* handle,
|
||||
const struct sockaddr_in6* multicast_addr,
|
||||
const char* interface_addr,
|
||||
uv_membership membership) {
|
||||
int optname;
|
||||
struct ipv6_mreq mreq;
|
||||
struct sockaddr_in6 addr6;
|
||||
|
||||
memset(&mreq, 0, sizeof mreq);
|
||||
|
||||
if (interface_addr) {
|
||||
if (uv_ip6_addr(interface_addr, 0, &addr6))
|
||||
return -EINVAL;
|
||||
mreq.ipv6mr_interface = addr6.sin6_scope_id;
|
||||
} else {
|
||||
mreq.ipv6mr_interface = 0;
|
||||
}
|
||||
|
||||
mreq.ipv6mr_multiaddr = multicast_addr->sin6_addr;
|
||||
|
||||
switch (membership) {
|
||||
case UV_JOIN_GROUP:
|
||||
optname = IPV6_ADD_MEMBERSHIP;
|
||||
break;
|
||||
case UV_LEAVE_GROUP:
|
||||
optname = IPV6_DROP_MEMBERSHIP;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (setsockopt(handle->io_watcher.fd,
|
||||
IPPROTO_IPV6,
|
||||
optname,
|
||||
&mreq,
|
||||
sizeof(mreq))) {
|
||||
#if defined(__MVS__)
|
||||
if (errno == ENXIO)
|
||||
return -ENODEV;
|
||||
#endif
|
||||
return -errno;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv_udp_init_ex(uv_loop_t* loop, uv_udp_t* handle, unsigned int flags) {
|
||||
int domain;
|
||||
int err;
|
||||
int fd;
|
||||
|
||||
/* Use the lower 8 bits for the domain */
|
||||
domain = flags & 0xFF;
|
||||
if (domain != AF_INET && domain != AF_INET6 && domain != AF_UNSPEC)
|
||||
return -EINVAL;
|
||||
|
||||
if (flags & ~0xFF)
|
||||
return -EINVAL;
|
||||
|
||||
if (domain != AF_UNSPEC) {
|
||||
err = uv__socket(domain, SOCK_DGRAM, 0);
|
||||
if (err < 0)
|
||||
return err;
|
||||
fd = err;
|
||||
} else {
|
||||
fd = -1;
|
||||
}
|
||||
|
||||
uv__handle_init(loop, (uv_handle_t*)handle, UV_UDP);
|
||||
handle->alloc_cb = NULL;
|
||||
handle->recv_cb = NULL;
|
||||
handle->send_queue_size = 0;
|
||||
handle->send_queue_count = 0;
|
||||
uv__io_init(&handle->io_watcher, uv__udp_io, fd);
|
||||
QUEUE_INIT(&handle->write_queue);
|
||||
QUEUE_INIT(&handle->write_completed_queue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv_udp_init(uv_loop_t* loop, uv_udp_t* handle) {
|
||||
return uv_udp_init_ex(loop, handle, AF_UNSPEC);
|
||||
}
|
||||
|
||||
|
||||
int uv_udp_open(uv_udp_t* handle, uv_os_sock_t sock) {
|
||||
int err;
|
||||
|
||||
/* Check for already active socket. */
|
||||
if (handle->io_watcher.fd != -1)
|
||||
return -EBUSY;
|
||||
|
||||
err = uv__nonblock(sock, 1);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = uv__set_reuse(sock);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
handle->io_watcher.fd = sock;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv_udp_set_membership(uv_udp_t* handle,
|
||||
const char* multicast_addr,
|
||||
const char* interface_addr,
|
||||
uv_membership membership) {
|
||||
int err;
|
||||
struct sockaddr_in addr4;
|
||||
struct sockaddr_in6 addr6;
|
||||
|
||||
if (uv_ip4_addr(multicast_addr, 0, &addr4) == 0) {
|
||||
err = uv__udp_maybe_deferred_bind(handle, AF_INET, UV_UDP_REUSEADDR);
|
||||
if (err)
|
||||
return err;
|
||||
return uv__udp_set_membership4(handle, &addr4, interface_addr, membership);
|
||||
} else if (uv_ip6_addr(multicast_addr, 0, &addr6) == 0) {
|
||||
err = uv__udp_maybe_deferred_bind(handle, AF_INET6, UV_UDP_REUSEADDR);
|
||||
if (err)
|
||||
return err;
|
||||
return uv__udp_set_membership6(handle, &addr6, interface_addr, membership);
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int uv__setsockopt(uv_udp_t* handle,
|
||||
int option4,
|
||||
int option6,
|
||||
const void* val,
|
||||
size_t size) {
|
||||
int r;
|
||||
|
||||
if (handle->flags & UV_HANDLE_IPV6)
|
||||
r = setsockopt(handle->io_watcher.fd,
|
||||
IPPROTO_IPV6,
|
||||
option6,
|
||||
val,
|
||||
size);
|
||||
else
|
||||
r = setsockopt(handle->io_watcher.fd,
|
||||
IPPROTO_IP,
|
||||
option4,
|
||||
val,
|
||||
size);
|
||||
if (r)
|
||||
return -errno;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int uv__setsockopt_maybe_char(uv_udp_t* handle,
|
||||
int option4,
|
||||
int option6,
|
||||
int val) {
|
||||
#if defined(__sun) || defined(_AIX) || defined(__MVS__)
|
||||
char arg = val;
|
||||
#elif defined(__OpenBSD__)
|
||||
unsigned char arg = val;
|
||||
#else
|
||||
int arg = val;
|
||||
#endif
|
||||
|
||||
if (val < 0 || val > 255)
|
||||
return -EINVAL;
|
||||
|
||||
return uv__setsockopt(handle, option4, option6, &arg, sizeof(arg));
|
||||
}
|
||||
|
||||
|
||||
int uv_udp_set_broadcast(uv_udp_t* handle, int on) {
|
||||
if (setsockopt(handle->io_watcher.fd,
|
||||
SOL_SOCKET,
|
||||
SO_BROADCAST,
|
||||
&on,
|
||||
sizeof(on))) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv_udp_set_ttl(uv_udp_t* handle, int ttl) {
|
||||
if (ttl < 1 || ttl > 255)
|
||||
return -EINVAL;
|
||||
|
||||
#if defined(__MVS__)
|
||||
if (!(handle->flags & UV_HANDLE_IPV6))
|
||||
return -ENOTSUP; /* zOS does not support setting ttl for IPv4 */
|
||||
#endif
|
||||
|
||||
/*
|
||||
* On Solaris and derivatives such as SmartOS, the length of socket options
|
||||
* is sizeof(int) for IP_TTL and IPV6_UNICAST_HOPS,
|
||||
* so hardcode the size of these options on this platform,
|
||||
* and use the general uv__setsockopt_maybe_char call on other platforms.
|
||||
*/
|
||||
#if defined(__sun) || defined(_AIX) || defined(__OpenBSD__) || \
|
||||
defined(__MVS__)
|
||||
|
||||
return uv__setsockopt(handle,
|
||||
IP_TTL,
|
||||
IPV6_UNICAST_HOPS,
|
||||
&ttl,
|
||||
sizeof(ttl));
|
||||
#endif /* defined(__sun) || defined(_AIX) || defined (__OpenBSD__) ||
|
||||
defined(__MVS__) */
|
||||
|
||||
return uv__setsockopt_maybe_char(handle,
|
||||
IP_TTL,
|
||||
IPV6_UNICAST_HOPS,
|
||||
ttl);
|
||||
}
|
||||
|
||||
|
||||
int uv_udp_set_multicast_ttl(uv_udp_t* handle, int ttl) {
|
||||
/*
|
||||
* On Solaris and derivatives such as SmartOS, the length of socket options
|
||||
* is sizeof(int) for IPV6_MULTICAST_HOPS and sizeof(char) for
|
||||
* IP_MULTICAST_TTL, so hardcode the size of the option in the IPv6 case,
|
||||
* and use the general uv__setsockopt_maybe_char call otherwise.
|
||||
*/
|
||||
#if defined(__sun) || defined(_AIX) || defined(__MVS__)
|
||||
if (handle->flags & UV_HANDLE_IPV6)
|
||||
return uv__setsockopt(handle,
|
||||
IP_MULTICAST_TTL,
|
||||
IPV6_MULTICAST_HOPS,
|
||||
&ttl,
|
||||
sizeof(ttl));
|
||||
#endif /* defined(__sun) || defined(_AIX) || defined(__MVS__) */
|
||||
|
||||
return uv__setsockopt_maybe_char(handle,
|
||||
IP_MULTICAST_TTL,
|
||||
IPV6_MULTICAST_HOPS,
|
||||
ttl);
|
||||
}
|
||||
|
||||
|
||||
int uv_udp_set_multicast_loop(uv_udp_t* handle, int on) {
|
||||
/*
|
||||
* On Solaris and derivatives such as SmartOS, the length of socket options
|
||||
* is sizeof(int) for IPV6_MULTICAST_LOOP and sizeof(char) for
|
||||
* IP_MULTICAST_LOOP, so hardcode the size of the option in the IPv6 case,
|
||||
* and use the general uv__setsockopt_maybe_char call otherwise.
|
||||
*/
|
||||
#if defined(__sun) || defined(_AIX) || defined(__MVS__)
|
||||
if (handle->flags & UV_HANDLE_IPV6)
|
||||
return uv__setsockopt(handle,
|
||||
IP_MULTICAST_LOOP,
|
||||
IPV6_MULTICAST_LOOP,
|
||||
&on,
|
||||
sizeof(on));
|
||||
#endif /* defined(__sun) || defined(_AIX) || defined(__MVS__) */
|
||||
|
||||
return uv__setsockopt_maybe_char(handle,
|
||||
IP_MULTICAST_LOOP,
|
||||
IPV6_MULTICAST_LOOP,
|
||||
on);
|
||||
}
|
||||
|
||||
int uv_udp_set_multicast_interface(uv_udp_t* handle, const char* interface_addr) {
|
||||
struct sockaddr_storage addr_st;
|
||||
struct sockaddr_in* addr4;
|
||||
struct sockaddr_in6* addr6;
|
||||
|
||||
addr4 = (struct sockaddr_in*) &addr_st;
|
||||
addr6 = (struct sockaddr_in6*) &addr_st;
|
||||
|
||||
if (!interface_addr) {
|
||||
memset(&addr_st, 0, sizeof addr_st);
|
||||
if (handle->flags & UV_HANDLE_IPV6) {
|
||||
addr_st.ss_family = AF_INET6;
|
||||
addr6->sin6_scope_id = 0;
|
||||
} else {
|
||||
addr_st.ss_family = AF_INET;
|
||||
addr4->sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
}
|
||||
} else if (uv_ip4_addr(interface_addr, 0, addr4) == 0) {
|
||||
/* nothing, address was parsed */
|
||||
} else if (uv_ip6_addr(interface_addr, 0, addr6) == 0) {
|
||||
/* nothing, address was parsed */
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (addr_st.ss_family == AF_INET) {
|
||||
if (setsockopt(handle->io_watcher.fd,
|
||||
IPPROTO_IP,
|
||||
IP_MULTICAST_IF,
|
||||
(void*) &addr4->sin_addr,
|
||||
sizeof(addr4->sin_addr)) == -1) {
|
||||
return -errno;
|
||||
}
|
||||
} else if (addr_st.ss_family == AF_INET6) {
|
||||
if (setsockopt(handle->io_watcher.fd,
|
||||
IPPROTO_IPV6,
|
||||
IPV6_MULTICAST_IF,
|
||||
&addr6->sin6_scope_id,
|
||||
sizeof(addr6->sin6_scope_id)) == -1) {
|
||||
return -errno;
|
||||
}
|
||||
} else {
|
||||
assert(0 && "unexpected address family");
|
||||
abort();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv_udp_getsockname(const uv_udp_t* handle,
|
||||
struct sockaddr* name,
|
||||
int* namelen) {
|
||||
socklen_t socklen;
|
||||
|
||||
if (handle->io_watcher.fd == -1)
|
||||
return -EINVAL; /* FIXME(bnoordhuis) -EBADF */
|
||||
|
||||
/* sizeof(socklen_t) != sizeof(int) on some systems. */
|
||||
socklen = (socklen_t) *namelen;
|
||||
|
||||
if (getsockname(handle->io_watcher.fd, name, &socklen))
|
||||
return -errno;
|
||||
|
||||
*namelen = (int) socklen;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv__udp_recv_start(uv_udp_t* handle,
|
||||
uv_alloc_cb alloc_cb,
|
||||
uv_udp_recv_cb recv_cb) {
|
||||
int err;
|
||||
|
||||
if (alloc_cb == NULL || recv_cb == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
if (uv__io_active(&handle->io_watcher, POLLIN))
|
||||
return -EALREADY; /* FIXME(bnoordhuis) Should be -EBUSY. */
|
||||
|
||||
err = uv__udp_maybe_deferred_bind(handle, AF_INET, 0);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
handle->alloc_cb = alloc_cb;
|
||||
handle->recv_cb = recv_cb;
|
||||
|
||||
uv__io_start(handle->loop, &handle->io_watcher, POLLIN);
|
||||
uv__handle_start(handle);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv__udp_recv_stop(uv_udp_t* handle) {
|
||||
uv__io_stop(handle->loop, &handle->io_watcher, POLLIN);
|
||||
|
||||
if (!uv__io_active(&handle->io_watcher, POLLOUT))
|
||||
uv__handle_stop(handle);
|
||||
|
||||
handle->alloc_cb = NULL;
|
||||
handle->recv_cb = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef UV_AIX_H
|
||||
#define UV_AIX_H
|
||||
|
||||
#define UV_PLATFORM_LOOP_FIELDS \
|
||||
int fs_fd; \
|
||||
|
||||
#define UV_PLATFORM_FS_EVENT_FIELDS \
|
||||
uv__io_t event_watcher; \
|
||||
char *dir_filename; \
|
||||
|
||||
#endif /* UV_AIX_H */
|
||||
@@ -0,0 +1,46 @@
|
||||
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef UV_BSD_H
|
||||
#define UV_BSD_H
|
||||
|
||||
#define UV_PLATFORM_FS_EVENT_FIELDS \
|
||||
uv__io_t event_watcher; \
|
||||
|
||||
#define UV_IO_PRIVATE_PLATFORM_FIELDS \
|
||||
int rcount; \
|
||||
int wcount; \
|
||||
|
||||
/*
|
||||
* NEW
|
||||
*/
|
||||
#define UV_PLATFORM_LOOP_FIELDS \
|
||||
struct pollfd* poll_fds; \
|
||||
size_t poll_fds_used; \
|
||||
size_t poll_fds_size; \
|
||||
unsigned char poll_fds_iterating; \
|
||||
/*
|
||||
*
|
||||
*/
|
||||
|
||||
#define UV_HAVE_KQUEUE 1
|
||||
|
||||
#endif /* UV_BSD_H */
|
||||
@@ -0,0 +1,664 @@
|
||||
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "uv.h"
|
||||
#include "uv-common.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h> /* NULL */
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h> /* malloc */
|
||||
#include <string.h> /* memset */
|
||||
|
||||
#if defined(_WIN32)
|
||||
# include <malloc.h> /* malloc */
|
||||
#else
|
||||
# include <net/if.h> /* if_nametoindex */
|
||||
#endif
|
||||
|
||||
|
||||
typedef struct {
|
||||
uv_malloc_func local_malloc;
|
||||
uv_realloc_func local_realloc;
|
||||
uv_calloc_func local_calloc;
|
||||
uv_free_func local_free;
|
||||
} uv__allocator_t;
|
||||
|
||||
static uv__allocator_t uv__allocator = {
|
||||
malloc,
|
||||
realloc,
|
||||
calloc,
|
||||
free,
|
||||
};
|
||||
|
||||
char* uv__strdup(const char* s) {
|
||||
size_t len = strlen(s) + 1;
|
||||
char* m = uv__malloc(len);
|
||||
if (m == NULL)
|
||||
return NULL;
|
||||
return memcpy(m, s, len);
|
||||
}
|
||||
|
||||
char* uv__strndup(const char* s, size_t n) {
|
||||
char* m;
|
||||
size_t len = strlen(s);
|
||||
if (n < len)
|
||||
len = n;
|
||||
m = uv__malloc(len + 1);
|
||||
if (m == NULL)
|
||||
return NULL;
|
||||
m[len] = '\0';
|
||||
return memcpy(m, s, len);
|
||||
}
|
||||
|
||||
void* uv__malloc(size_t size) {
|
||||
return uv__allocator.local_malloc(size);
|
||||
}
|
||||
|
||||
void uv__free(void* ptr) {
|
||||
int saved_errno;
|
||||
|
||||
/* Libuv expects that free() does not clobber errno. The system allocator
|
||||
* honors that assumption but custom allocators may not be so careful.
|
||||
*/
|
||||
saved_errno = errno;
|
||||
uv__allocator.local_free(ptr);
|
||||
errno = saved_errno;
|
||||
}
|
||||
|
||||
void* uv__calloc(size_t count, size_t size) {
|
||||
return uv__allocator.local_calloc(count, size);
|
||||
}
|
||||
|
||||
void* uv__realloc(void* ptr, size_t size) {
|
||||
return uv__allocator.local_realloc(ptr, size);
|
||||
}
|
||||
|
||||
int uv_replace_allocator(uv_malloc_func malloc_func,
|
||||
uv_realloc_func realloc_func,
|
||||
uv_calloc_func calloc_func,
|
||||
uv_free_func free_func) {
|
||||
if (malloc_func == NULL || realloc_func == NULL ||
|
||||
calloc_func == NULL || free_func == NULL) {
|
||||
return UV_EINVAL;
|
||||
}
|
||||
|
||||
uv__allocator.local_malloc = malloc_func;
|
||||
uv__allocator.local_realloc = realloc_func;
|
||||
uv__allocator.local_calloc = calloc_func;
|
||||
uv__allocator.local_free = free_func;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define XX(uc, lc) case UV_##uc: return sizeof(uv_##lc##_t);
|
||||
|
||||
size_t uv_handle_size(uv_handle_type type) {
|
||||
switch (type) {
|
||||
UV_HANDLE_TYPE_MAP(XX)
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
size_t uv_req_size(uv_req_type type) {
|
||||
switch(type) {
|
||||
UV_REQ_TYPE_MAP(XX)
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
#undef XX
|
||||
|
||||
|
||||
size_t uv_loop_size(void) {
|
||||
return sizeof(uv_loop_t);
|
||||
}
|
||||
|
||||
|
||||
uv_buf_t uv_buf_init(char* base, unsigned int len) {
|
||||
uv_buf_t buf;
|
||||
buf.base = base;
|
||||
buf.len = len;
|
||||
return buf;
|
||||
}
|
||||
|
||||
|
||||
static const char* uv__unknown_err_code(int err) {
|
||||
char buf[32];
|
||||
char* copy;
|
||||
|
||||
snprintf(buf, sizeof(buf), "Unknown system error %d", err);
|
||||
copy = uv__strdup(buf);
|
||||
|
||||
return copy != NULL ? copy : "Unknown system error";
|
||||
}
|
||||
|
||||
|
||||
#define UV_ERR_NAME_GEN(name, _) case UV_ ## name: return #name;
|
||||
const char* uv_err_name(int err) {
|
||||
switch (err) {
|
||||
UV_ERRNO_MAP(UV_ERR_NAME_GEN)
|
||||
}
|
||||
return uv__unknown_err_code(err);
|
||||
}
|
||||
#undef UV_ERR_NAME_GEN
|
||||
|
||||
|
||||
#define UV_STRERROR_GEN(name, msg) case UV_ ## name: return msg;
|
||||
const char* uv_strerror(int err) {
|
||||
switch (err) {
|
||||
UV_ERRNO_MAP(UV_STRERROR_GEN)
|
||||
}
|
||||
return uv__unknown_err_code(err);
|
||||
}
|
||||
#undef UV_STRERROR_GEN
|
||||
|
||||
|
||||
int uv_ip4_addr(const char* ip, int port, struct sockaddr_in* addr) {
|
||||
memset(addr, 0, sizeof(*addr));
|
||||
addr->sin_family = AF_INET;
|
||||
addr->sin_port = htons(port);
|
||||
return uv_inet_pton(AF_INET, ip, &(addr->sin_addr.s_addr));
|
||||
}
|
||||
|
||||
|
||||
int uv_ip6_addr(const char* ip, int port, struct sockaddr_in6* addr) {
|
||||
char address_part[40];
|
||||
size_t address_part_size;
|
||||
const char* zone_index;
|
||||
|
||||
memset(addr, 0, sizeof(*addr));
|
||||
addr->sin6_family = AF_INET6;
|
||||
addr->sin6_port = htons(port);
|
||||
|
||||
zone_index = strchr(ip, '%');
|
||||
if (zone_index != NULL) {
|
||||
address_part_size = zone_index - ip;
|
||||
if (address_part_size >= sizeof(address_part))
|
||||
address_part_size = sizeof(address_part) - 1;
|
||||
|
||||
memcpy(address_part, ip, address_part_size);
|
||||
address_part[address_part_size] = '\0';
|
||||
ip = address_part;
|
||||
|
||||
zone_index++; /* skip '%' */
|
||||
/* NOTE: unknown interface (id=0) is silently ignored */
|
||||
#ifdef _WIN32
|
||||
addr->sin6_scope_id = atoi(zone_index);
|
||||
#else
|
||||
addr->sin6_scope_id = if_nametoindex(zone_index);
|
||||
#endif
|
||||
}
|
||||
|
||||
return uv_inet_pton(AF_INET6, ip, &addr->sin6_addr);
|
||||
}
|
||||
|
||||
|
||||
int uv_ip4_name(const struct sockaddr_in* src, char* dst, size_t size) {
|
||||
return uv_inet_ntop(AF_INET, &src->sin_addr, dst, size);
|
||||
}
|
||||
|
||||
|
||||
int uv_ip6_name(const struct sockaddr_in6* src, char* dst, size_t size) {
|
||||
return uv_inet_ntop(AF_INET6, &src->sin6_addr, dst, size);
|
||||
}
|
||||
|
||||
|
||||
int uv_tcp_bind(uv_tcp_t* handle,
|
||||
const struct sockaddr* addr,
|
||||
unsigned int flags) {
|
||||
unsigned int addrlen;
|
||||
|
||||
if (handle->type != UV_TCP)
|
||||
return UV_EINVAL;
|
||||
|
||||
if (addr->sa_family == AF_INET)
|
||||
addrlen = sizeof(struct sockaddr_in);
|
||||
else if (addr->sa_family == AF_INET6)
|
||||
addrlen = sizeof(struct sockaddr_in6);
|
||||
else
|
||||
return UV_EINVAL;
|
||||
|
||||
return uv__tcp_bind(handle, addr, addrlen, flags);
|
||||
}
|
||||
|
||||
|
||||
int uv_udp_bind(uv_udp_t* handle,
|
||||
const struct sockaddr* addr,
|
||||
unsigned int flags) {
|
||||
unsigned int addrlen;
|
||||
|
||||
if (handle->type != UV_UDP)
|
||||
return UV_EINVAL;
|
||||
|
||||
if (addr->sa_family == AF_INET)
|
||||
addrlen = sizeof(struct sockaddr_in);
|
||||
else if (addr->sa_family == AF_INET6)
|
||||
addrlen = sizeof(struct sockaddr_in6);
|
||||
else
|
||||
return UV_EINVAL;
|
||||
|
||||
return uv__udp_bind(handle, addr, addrlen, flags);
|
||||
}
|
||||
|
||||
|
||||
int uv_tcp_connect(uv_connect_t* req,
|
||||
uv_tcp_t* handle,
|
||||
const struct sockaddr* addr,
|
||||
uv_connect_cb cb) {
|
||||
unsigned int addrlen;
|
||||
|
||||
if (handle->type != UV_TCP)
|
||||
return UV_EINVAL;
|
||||
|
||||
if (addr->sa_family == AF_INET)
|
||||
addrlen = sizeof(struct sockaddr_in);
|
||||
else if (addr->sa_family == AF_INET6)
|
||||
addrlen = sizeof(struct sockaddr_in6);
|
||||
else
|
||||
return UV_EINVAL;
|
||||
|
||||
return uv__tcp_connect(req, handle, addr, addrlen, cb);
|
||||
}
|
||||
|
||||
|
||||
int uv_udp_send(uv_udp_send_t* req,
|
||||
uv_udp_t* handle,
|
||||
const uv_buf_t bufs[],
|
||||
unsigned int nbufs,
|
||||
const struct sockaddr* addr,
|
||||
uv_udp_send_cb send_cb) {
|
||||
unsigned int addrlen;
|
||||
|
||||
if (handle->type != UV_UDP)
|
||||
return UV_EINVAL;
|
||||
|
||||
if (addr->sa_family == AF_INET)
|
||||
addrlen = sizeof(struct sockaddr_in);
|
||||
else if (addr->sa_family == AF_INET6)
|
||||
addrlen = sizeof(struct sockaddr_in6);
|
||||
else
|
||||
return UV_EINVAL;
|
||||
|
||||
return uv__udp_send(req, handle, bufs, nbufs, addr, addrlen, send_cb);
|
||||
}
|
||||
|
||||
|
||||
int uv_udp_try_send(uv_udp_t* handle,
|
||||
const uv_buf_t bufs[],
|
||||
unsigned int nbufs,
|
||||
const struct sockaddr* addr) {
|
||||
unsigned int addrlen;
|
||||
|
||||
if (handle->type != UV_UDP)
|
||||
return UV_EINVAL;
|
||||
|
||||
if (addr->sa_family == AF_INET)
|
||||
addrlen = sizeof(struct sockaddr_in);
|
||||
else if (addr->sa_family == AF_INET6)
|
||||
addrlen = sizeof(struct sockaddr_in6);
|
||||
else
|
||||
return UV_EINVAL;
|
||||
|
||||
return uv__udp_try_send(handle, bufs, nbufs, addr, addrlen);
|
||||
}
|
||||
|
||||
|
||||
int uv_udp_recv_start(uv_udp_t* handle,
|
||||
uv_alloc_cb alloc_cb,
|
||||
uv_udp_recv_cb recv_cb) {
|
||||
if (handle->type != UV_UDP || alloc_cb == NULL || recv_cb == NULL)
|
||||
return UV_EINVAL;
|
||||
else
|
||||
return uv__udp_recv_start(handle, alloc_cb, recv_cb);
|
||||
}
|
||||
|
||||
|
||||
int uv_udp_recv_stop(uv_udp_t* handle) {
|
||||
if (handle->type != UV_UDP)
|
||||
return UV_EINVAL;
|
||||
else
|
||||
return uv__udp_recv_stop(handle);
|
||||
}
|
||||
|
||||
|
||||
void uv_walk(uv_loop_t* loop, uv_walk_cb walk_cb, void* arg) {
|
||||
QUEUE queue;
|
||||
QUEUE* q;
|
||||
uv_handle_t* h;
|
||||
|
||||
QUEUE_MOVE(&loop->handle_queue, &queue);
|
||||
while (!QUEUE_EMPTY(&queue)) {
|
||||
q = QUEUE_HEAD(&queue);
|
||||
h = QUEUE_DATA(q, uv_handle_t, handle_queue);
|
||||
|
||||
QUEUE_REMOVE(q);
|
||||
QUEUE_INSERT_TAIL(&loop->handle_queue, q);
|
||||
|
||||
if (h->flags & UV__HANDLE_INTERNAL) continue;
|
||||
walk_cb(h, arg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void uv__print_handles(uv_loop_t* loop, int only_active, FILE* stream) {
|
||||
const char* type;
|
||||
QUEUE* q;
|
||||
uv_handle_t* h;
|
||||
|
||||
if (loop == NULL)
|
||||
loop = uv_default_loop();
|
||||
|
||||
QUEUE_FOREACH(q, &loop->handle_queue) {
|
||||
h = QUEUE_DATA(q, uv_handle_t, handle_queue);
|
||||
|
||||
if (only_active && !uv__is_active(h))
|
||||
continue;
|
||||
|
||||
switch (h->type) {
|
||||
#define X(uc, lc) case UV_##uc: type = #lc; break;
|
||||
UV_HANDLE_TYPE_MAP(X)
|
||||
#undef X
|
||||
default: type = "<unknown>";
|
||||
}
|
||||
|
||||
fprintf(stream,
|
||||
"[%c%c%c] %-8s %p\n",
|
||||
"R-"[!(h->flags & UV__HANDLE_REF)],
|
||||
"A-"[!(h->flags & UV__HANDLE_ACTIVE)],
|
||||
"I-"[!(h->flags & UV__HANDLE_INTERNAL)],
|
||||
type,
|
||||
(void*)h);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void uv_print_all_handles(uv_loop_t* loop, FILE* stream) {
|
||||
uv__print_handles(loop, 0, stream);
|
||||
}
|
||||
|
||||
|
||||
void uv_print_active_handles(uv_loop_t* loop, FILE* stream) {
|
||||
uv__print_handles(loop, 1, stream);
|
||||
}
|
||||
|
||||
|
||||
void uv_ref(uv_handle_t* handle) {
|
||||
uv__handle_ref(handle);
|
||||
}
|
||||
|
||||
|
||||
void uv_unref(uv_handle_t* handle) {
|
||||
uv__handle_unref(handle);
|
||||
}
|
||||
|
||||
|
||||
int uv_has_ref(const uv_handle_t* handle) {
|
||||
return uv__has_ref(handle);
|
||||
}
|
||||
|
||||
|
||||
void uv_stop(uv_loop_t* loop) {
|
||||
loop->stop_flag = 1;
|
||||
}
|
||||
|
||||
|
||||
uint64_t uv_now(const uv_loop_t* loop) {
|
||||
return loop->time;
|
||||
}
|
||||
|
||||
|
||||
|
||||
size_t uv__count_bufs(const uv_buf_t bufs[], unsigned int nbufs) {
|
||||
unsigned int i;
|
||||
size_t bytes;
|
||||
|
||||
bytes = 0;
|
||||
for (i = 0; i < nbufs; i++)
|
||||
bytes += (size_t) bufs[i].len;
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
int uv_recv_buffer_size(uv_handle_t* handle, int* value) {
|
||||
return uv__socket_sockopt(handle, SO_RCVBUF, value);
|
||||
}
|
||||
|
||||
int uv_send_buffer_size(uv_handle_t* handle, int *value) {
|
||||
return uv__socket_sockopt(handle, SO_SNDBUF, value);
|
||||
}
|
||||
|
||||
int uv_fs_event_getpath(uv_fs_event_t* handle, char* buffer, size_t* size) {
|
||||
size_t required_len;
|
||||
|
||||
if (!uv__is_active(handle)) {
|
||||
*size = 0;
|
||||
return UV_EINVAL;
|
||||
}
|
||||
|
||||
required_len = strlen(handle->path);
|
||||
if (required_len >= *size) {
|
||||
*size = required_len + 1;
|
||||
return UV_ENOBUFS;
|
||||
}
|
||||
|
||||
memcpy(buffer, handle->path, required_len);
|
||||
*size = required_len;
|
||||
buffer[required_len] = '\0';
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* The windows implementation does not have the same structure layout as
|
||||
* the unix implementation (nbufs is not directly inside req but is
|
||||
* contained in a nested union/struct) so this function locates it.
|
||||
*/
|
||||
static unsigned int* uv__get_nbufs(uv_fs_t* req) {
|
||||
#ifdef _WIN32
|
||||
return &req->fs.info.nbufs;
|
||||
#else
|
||||
return &req->nbufs;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* uv_fs_scandir() uses the system allocator to allocate memory on non-Windows
|
||||
* systems. So, the memory should be released using free(). On Windows,
|
||||
* uv__malloc() is used, so use uv__free() to free memory.
|
||||
*/
|
||||
#ifdef _WIN32
|
||||
# define uv__fs_scandir_free uv__free
|
||||
#else
|
||||
# define uv__fs_scandir_free free
|
||||
#endif
|
||||
|
||||
void uv__fs_scandir_cleanup(uv_fs_t* req) {
|
||||
uv__dirent_t** dents;
|
||||
|
||||
unsigned int* nbufs = uv__get_nbufs(req);
|
||||
|
||||
dents = req->ptr;
|
||||
if (*nbufs > 0 && *nbufs != (unsigned int) req->result)
|
||||
(*nbufs)--;
|
||||
for (; *nbufs < (unsigned int) req->result; (*nbufs)++)
|
||||
uv__fs_scandir_free(dents[*nbufs]);
|
||||
|
||||
uv__fs_scandir_free(req->ptr);
|
||||
req->ptr = NULL;
|
||||
}
|
||||
|
||||
|
||||
int uv_fs_scandir_next(uv_fs_t* req, uv_dirent_t* ent) {
|
||||
uv__dirent_t** dents;
|
||||
uv__dirent_t* dent;
|
||||
unsigned int* nbufs;
|
||||
|
||||
/* Check to see if req passed */
|
||||
if (req->result < 0)
|
||||
return req->result;
|
||||
|
||||
/* Ptr will be null if req was canceled or no files found */
|
||||
if (!req->ptr)
|
||||
return UV_EOF;
|
||||
|
||||
nbufs = uv__get_nbufs(req);
|
||||
assert(nbufs);
|
||||
|
||||
dents = req->ptr;
|
||||
|
||||
/* Free previous entity */
|
||||
if (*nbufs > 0)
|
||||
uv__fs_scandir_free(dents[*nbufs - 1]);
|
||||
|
||||
/* End was already reached */
|
||||
if (*nbufs == (unsigned int) req->result) {
|
||||
uv__fs_scandir_free(dents);
|
||||
req->ptr = NULL;
|
||||
return UV_EOF;
|
||||
}
|
||||
|
||||
dent = dents[(*nbufs)++];
|
||||
|
||||
ent->name = dent->d_name;
|
||||
#ifdef HAVE_DIRENT_TYPES
|
||||
switch (dent->d_type) {
|
||||
case UV__DT_DIR:
|
||||
ent->type = UV_DIRENT_DIR;
|
||||
break;
|
||||
case UV__DT_FILE:
|
||||
ent->type = UV_DIRENT_FILE;
|
||||
break;
|
||||
case UV__DT_LINK:
|
||||
ent->type = UV_DIRENT_LINK;
|
||||
break;
|
||||
case UV__DT_FIFO:
|
||||
ent->type = UV_DIRENT_FIFO;
|
||||
break;
|
||||
case UV__DT_SOCKET:
|
||||
ent->type = UV_DIRENT_SOCKET;
|
||||
break;
|
||||
case UV__DT_CHAR:
|
||||
ent->type = UV_DIRENT_CHAR;
|
||||
break;
|
||||
case UV__DT_BLOCK:
|
||||
ent->type = UV_DIRENT_BLOCK;
|
||||
break;
|
||||
default:
|
||||
ent->type = UV_DIRENT_UNKNOWN;
|
||||
}
|
||||
#else
|
||||
ent->type = UV_DIRENT_UNKNOWN;
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv_loop_configure(uv_loop_t* loop, uv_loop_option option, ...) {
|
||||
va_list ap;
|
||||
int err;
|
||||
|
||||
va_start(ap, option);
|
||||
/* Any platform-agnostic options should be handled here. */
|
||||
err = uv__loop_configure(loop, option, ap);
|
||||
va_end(ap);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
static uv_loop_t default_loop_struct;
|
||||
static uv_loop_t* default_loop_ptr;
|
||||
|
||||
|
||||
uv_loop_t* uv_default_loop(void) {
|
||||
if (default_loop_ptr != NULL)
|
||||
return default_loop_ptr;
|
||||
|
||||
if (uv_loop_init(&default_loop_struct))
|
||||
return NULL;
|
||||
|
||||
default_loop_ptr = &default_loop_struct;
|
||||
return default_loop_ptr;
|
||||
}
|
||||
|
||||
|
||||
uv_loop_t* uv_loop_new(void) {
|
||||
uv_loop_t* loop;
|
||||
|
||||
loop = uv__malloc(sizeof(*loop));
|
||||
if (loop == NULL)
|
||||
return NULL;
|
||||
|
||||
if (uv_loop_init(loop)) {
|
||||
uv__free(loop);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return loop;
|
||||
}
|
||||
|
||||
|
||||
int uv_loop_close(uv_loop_t* loop) {
|
||||
QUEUE* q;
|
||||
uv_handle_t* h;
|
||||
#ifndef NDEBUG
|
||||
void* saved_data;
|
||||
#endif
|
||||
|
||||
if (!QUEUE_EMPTY(&(loop)->active_reqs))
|
||||
return UV_EBUSY;
|
||||
|
||||
QUEUE_FOREACH(q, &loop->handle_queue) {
|
||||
h = QUEUE_DATA(q, uv_handle_t, handle_queue);
|
||||
if (!(h->flags & UV__HANDLE_INTERNAL))
|
||||
return UV_EBUSY;
|
||||
}
|
||||
|
||||
uv__loop_close(loop);
|
||||
|
||||
#ifndef NDEBUG
|
||||
saved_data = loop->data;
|
||||
memset(loop, -1, sizeof(*loop));
|
||||
loop->data = saved_data;
|
||||
#endif
|
||||
if (loop == default_loop_ptr)
|
||||
default_loop_ptr = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void uv_loop_delete(uv_loop_t* loop) {
|
||||
uv_loop_t* default_loop;
|
||||
int err;
|
||||
|
||||
default_loop = default_loop_ptr;
|
||||
|
||||
err = uv_loop_close(loop);
|
||||
(void) err; /* Squelch compiler warnings. */
|
||||
assert(err == 0);
|
||||
if (loop != default_loop)
|
||||
uv__free(loop);
|
||||
}
|
||||
@@ -0,0 +1,254 @@
|
||||
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is private to libuv. It provides common functionality to both
|
||||
* Windows and Unix backends.
|
||||
*/
|
||||
|
||||
#ifndef UV_COMMON_H_
|
||||
#define UV_COMMON_H_
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#if defined(_MSC_VER) && _MSC_VER < 1600
|
||||
# include "stdint-msvc2008.h"
|
||||
#else
|
||||
# include <stdint.h>
|
||||
#endif
|
||||
|
||||
#include "uv.h"
|
||||
#include "tree.h"
|
||||
#include "queue.h"
|
||||
|
||||
#if !defined(snprintf) && defined(_MSC_VER) && _MSC_VER < 1900
|
||||
extern int snprintf(char*, size_t, const char*, ...);
|
||||
#endif
|
||||
|
||||
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
|
||||
|
||||
#define container_of(ptr, type, member) \
|
||||
((type *) ((char *) (ptr) - offsetof(type, member)))
|
||||
|
||||
#define STATIC_ASSERT(expr) \
|
||||
void uv__static_assert(int static_assert_failed[1 - 2 * !(expr)])
|
||||
|
||||
#ifndef _WIN32
|
||||
enum {
|
||||
UV__SIGNAL_ONE_SHOT = 0x80000, /* On signal reception remove sighandler */
|
||||
UV__HANDLE_INTERNAL = 0x8000,
|
||||
UV__HANDLE_ACTIVE = 0x4000,
|
||||
UV__HANDLE_REF = 0x2000,
|
||||
UV__HANDLE_CLOSING = 0 /* no-op on unix */
|
||||
};
|
||||
#else
|
||||
# define UV__SIGNAL_ONE_SHOT_DISPATCHED 0x200
|
||||
# define UV__SIGNAL_ONE_SHOT 0x100
|
||||
# define UV__HANDLE_INTERNAL 0x80
|
||||
# define UV__HANDLE_ACTIVE 0x40
|
||||
# define UV__HANDLE_REF 0x20
|
||||
# define UV__HANDLE_CLOSING 0x01
|
||||
#endif
|
||||
|
||||
int uv__loop_configure(uv_loop_t* loop, uv_loop_option option, va_list ap);
|
||||
|
||||
void uv__loop_close(uv_loop_t* loop);
|
||||
|
||||
int uv__tcp_bind(uv_tcp_t* tcp,
|
||||
const struct sockaddr* addr,
|
||||
unsigned int addrlen,
|
||||
unsigned int flags);
|
||||
|
||||
int uv__tcp_connect(uv_connect_t* req,
|
||||
uv_tcp_t* handle,
|
||||
const struct sockaddr* addr,
|
||||
unsigned int addrlen,
|
||||
uv_connect_cb cb);
|
||||
|
||||
int uv__udp_bind(uv_udp_t* handle,
|
||||
const struct sockaddr* addr,
|
||||
unsigned int addrlen,
|
||||
unsigned int flags);
|
||||
|
||||
int uv__udp_send(uv_udp_send_t* req,
|
||||
uv_udp_t* handle,
|
||||
const uv_buf_t bufs[],
|
||||
unsigned int nbufs,
|
||||
const struct sockaddr* addr,
|
||||
unsigned int addrlen,
|
||||
uv_udp_send_cb send_cb);
|
||||
|
||||
int uv__udp_try_send(uv_udp_t* handle,
|
||||
const uv_buf_t bufs[],
|
||||
unsigned int nbufs,
|
||||
const struct sockaddr* addr,
|
||||
unsigned int addrlen);
|
||||
|
||||
int uv__udp_recv_start(uv_udp_t* handle, uv_alloc_cb alloccb,
|
||||
uv_udp_recv_cb recv_cb);
|
||||
|
||||
int uv__udp_recv_stop(uv_udp_t* handle);
|
||||
|
||||
void uv__fs_poll_close(uv_fs_poll_t* handle);
|
||||
|
||||
int uv__getaddrinfo_translate_error(int sys_err); /* EAI_* error. */
|
||||
|
||||
void uv__work_submit(uv_loop_t* loop,
|
||||
struct uv__work *w,
|
||||
void (*work)(struct uv__work *w),
|
||||
void (*done)(struct uv__work *w, int status));
|
||||
|
||||
void uv__work_done(uv_async_t* handle);
|
||||
|
||||
size_t uv__count_bufs(const uv_buf_t bufs[], unsigned int nbufs);
|
||||
|
||||
int uv__socket_sockopt(uv_handle_t* handle, int optname, int* value);
|
||||
|
||||
void uv__fs_scandir_cleanup(uv_fs_t* req);
|
||||
|
||||
#define uv__has_active_reqs(loop) \
|
||||
(QUEUE_EMPTY(&(loop)->active_reqs) == 0)
|
||||
|
||||
#define uv__req_register(loop, req) \
|
||||
do { \
|
||||
QUEUE_INSERT_TAIL(&(loop)->active_reqs, &(req)->active_queue); \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
#define uv__req_unregister(loop, req) \
|
||||
do { \
|
||||
assert(uv__has_active_reqs(loop)); \
|
||||
QUEUE_REMOVE(&(req)->active_queue); \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
#define uv__has_active_handles(loop) \
|
||||
((loop)->active_handles > 0)
|
||||
|
||||
#define uv__active_handle_add(h) \
|
||||
do { \
|
||||
(h)->loop->active_handles++; \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
#define uv__active_handle_rm(h) \
|
||||
do { \
|
||||
(h)->loop->active_handles--; \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
#define uv__is_active(h) \
|
||||
(((h)->flags & UV__HANDLE_ACTIVE) != 0)
|
||||
|
||||
#define uv__is_closing(h) \
|
||||
(((h)->flags & (UV_CLOSING | UV_CLOSED)) != 0)
|
||||
|
||||
#define uv__handle_start(h) \
|
||||
do { \
|
||||
assert(((h)->flags & UV__HANDLE_CLOSING) == 0); \
|
||||
if (((h)->flags & UV__HANDLE_ACTIVE) != 0) break; \
|
||||
(h)->flags |= UV__HANDLE_ACTIVE; \
|
||||
if (((h)->flags & UV__HANDLE_REF) != 0) uv__active_handle_add(h); \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
#define uv__handle_stop(h) \
|
||||
do { \
|
||||
assert(((h)->flags & UV__HANDLE_CLOSING) == 0); \
|
||||
if (((h)->flags & UV__HANDLE_ACTIVE) == 0) break; \
|
||||
(h)->flags &= ~UV__HANDLE_ACTIVE; \
|
||||
if (((h)->flags & UV__HANDLE_REF) != 0) uv__active_handle_rm(h); \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
#define uv__handle_ref(h) \
|
||||
do { \
|
||||
if (((h)->flags & UV__HANDLE_REF) != 0) break; \
|
||||
(h)->flags |= UV__HANDLE_REF; \
|
||||
if (((h)->flags & UV__HANDLE_CLOSING) != 0) break; \
|
||||
if (((h)->flags & UV__HANDLE_ACTIVE) != 0) uv__active_handle_add(h); \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
#define uv__handle_unref(h) \
|
||||
do { \
|
||||
if (((h)->flags & UV__HANDLE_REF) == 0) break; \
|
||||
(h)->flags &= ~UV__HANDLE_REF; \
|
||||
if (((h)->flags & UV__HANDLE_CLOSING) != 0) break; \
|
||||
if (((h)->flags & UV__HANDLE_ACTIVE) != 0) uv__active_handle_rm(h); \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
#define uv__has_ref(h) \
|
||||
(((h)->flags & UV__HANDLE_REF) != 0)
|
||||
|
||||
#if defined(_WIN32)
|
||||
# define uv__handle_platform_init(h) ((h)->u.fd = -1)
|
||||
#else
|
||||
# define uv__handle_platform_init(h) ((h)->next_closing = NULL)
|
||||
#endif
|
||||
|
||||
#define uv__handle_init(loop_, h, type_) \
|
||||
do { \
|
||||
(h)->loop = (loop_); \
|
||||
(h)->type = (type_); \
|
||||
(h)->flags = UV__HANDLE_REF; /* Ref the loop when active. */ \
|
||||
QUEUE_INSERT_TAIL(&(loop_)->handle_queue, &(h)->handle_queue); \
|
||||
uv__handle_platform_init(h); \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
/* Note: uses an open-coded version of SET_REQ_SUCCESS() because of
|
||||
* a circular dependency between src/uv-common.h and src/win/internal.h.
|
||||
*/
|
||||
#if defined(_WIN32)
|
||||
# define UV_REQ_INIT(req, typ) \
|
||||
do { \
|
||||
(req)->type = (typ); \
|
||||
(req)->u.io.overlapped.Internal = 0; /* SET_REQ_SUCCESS() */ \
|
||||
} \
|
||||
while (0)
|
||||
#else
|
||||
# define UV_REQ_INIT(req, typ) \
|
||||
do { \
|
||||
(req)->type = (typ); \
|
||||
} \
|
||||
while (0)
|
||||
#endif
|
||||
|
||||
#define uv__req_init(loop, req, typ) \
|
||||
do { \
|
||||
UV_REQ_INIT(req, typ); \
|
||||
uv__req_register(loop, req); \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
/* Allocator prototypes */
|
||||
void *uv__calloc(size_t count, size_t size);
|
||||
char *uv__strdup(const char* s);
|
||||
char *uv__strndup(const char* s, size_t n);
|
||||
void* uv__malloc(size_t size);
|
||||
void uv__free(void* ptr);
|
||||
void* uv__realloc(void* ptr, size_t size);
|
||||
|
||||
#endif /* UV_COMMON_H_ */
|
||||
@@ -0,0 +1,61 @@
|
||||
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef UV_DARWIN_H
|
||||
#define UV_DARWIN_H
|
||||
|
||||
#if defined(__APPLE__) && defined(__MACH__)
|
||||
# include <mach/mach.h>
|
||||
# include <mach/task.h>
|
||||
# include <mach/semaphore.h>
|
||||
# include <TargetConditionals.h>
|
||||
# define UV_PLATFORM_SEM_T semaphore_t
|
||||
#endif
|
||||
|
||||
#define UV_IO_PRIVATE_PLATFORM_FIELDS \
|
||||
int rcount; \
|
||||
int wcount; \
|
||||
|
||||
#define UV_PLATFORM_LOOP_FIELDS \
|
||||
uv_thread_t cf_thread; \
|
||||
void* _cf_reserved; \
|
||||
void* cf_state; \
|
||||
uv_mutex_t cf_mutex; \
|
||||
uv_sem_t cf_sem; \
|
||||
void* cf_signals[2]; \
|
||||
|
||||
#define UV_PLATFORM_FS_EVENT_FIELDS \
|
||||
uv__io_t event_watcher; \
|
||||
char* realpath; \
|
||||
int realpath_len; \
|
||||
int cf_flags; \
|
||||
uv_async_t* cf_cb; \
|
||||
void* cf_events[2]; \
|
||||
void* cf_member[2]; \
|
||||
int cf_error; \
|
||||
uv_mutex_t cf_mutex; \
|
||||
|
||||
#define UV_STREAM_PRIVATE_PLATFORM_FIELDS \
|
||||
void* select; \
|
||||
|
||||
#define UV_HAVE_KQUEUE 1
|
||||
|
||||
#endif /* UV_DARWIN_H */
|
||||
@@ -0,0 +1,419 @@
|
||||
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef UV_ERRNO_H_
|
||||
#define UV_ERRNO_H_
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#define UV__EOF (-4095)
|
||||
#define UV__UNKNOWN (-4094)
|
||||
|
||||
#define UV__EAI_ADDRFAMILY (-3000)
|
||||
#define UV__EAI_AGAIN (-3001)
|
||||
#define UV__EAI_BADFLAGS (-3002)
|
||||
#define UV__EAI_CANCELED (-3003)
|
||||
#define UV__EAI_FAIL (-3004)
|
||||
#define UV__EAI_FAMILY (-3005)
|
||||
#define UV__EAI_MEMORY (-3006)
|
||||
#define UV__EAI_NODATA (-3007)
|
||||
#define UV__EAI_NONAME (-3008)
|
||||
#define UV__EAI_OVERFLOW (-3009)
|
||||
#define UV__EAI_SERVICE (-3010)
|
||||
#define UV__EAI_SOCKTYPE (-3011)
|
||||
#define UV__EAI_BADHINTS (-3013)
|
||||
#define UV__EAI_PROTOCOL (-3014)
|
||||
|
||||
/* Only map to the system errno on non-Windows platforms. It's apparently
|
||||
* a fairly common practice for Windows programmers to redefine errno codes.
|
||||
*/
|
||||
#if defined(E2BIG) && !defined(_WIN32)
|
||||
# define UV__E2BIG (-E2BIG)
|
||||
#else
|
||||
# define UV__E2BIG (-4093)
|
||||
#endif
|
||||
|
||||
#if defined(EACCES) && !defined(_WIN32)
|
||||
# define UV__EACCES (-EACCES)
|
||||
#else
|
||||
# define UV__EACCES (-4092)
|
||||
#endif
|
||||
|
||||
#if defined(EADDRINUSE) && !defined(_WIN32)
|
||||
# define UV__EADDRINUSE (-EADDRINUSE)
|
||||
#else
|
||||
# define UV__EADDRINUSE (-4091)
|
||||
#endif
|
||||
|
||||
#if defined(EADDRNOTAVAIL) && !defined(_WIN32)
|
||||
# define UV__EADDRNOTAVAIL (-EADDRNOTAVAIL)
|
||||
#else
|
||||
# define UV__EADDRNOTAVAIL (-4090)
|
||||
#endif
|
||||
|
||||
#if defined(EAFNOSUPPORT) && !defined(_WIN32)
|
||||
# define UV__EAFNOSUPPORT (-EAFNOSUPPORT)
|
||||
#else
|
||||
# define UV__EAFNOSUPPORT (-4089)
|
||||
#endif
|
||||
|
||||
#if defined(EAGAIN) && !defined(_WIN32)
|
||||
# define UV__EAGAIN (-EAGAIN)
|
||||
#else
|
||||
# define UV__EAGAIN (-4088)
|
||||
#endif
|
||||
|
||||
#if defined(EALREADY) && !defined(_WIN32)
|
||||
# define UV__EALREADY (-EALREADY)
|
||||
#else
|
||||
# define UV__EALREADY (-4084)
|
||||
#endif
|
||||
|
||||
#if defined(EBADF) && !defined(_WIN32)
|
||||
# define UV__EBADF (-EBADF)
|
||||
#else
|
||||
# define UV__EBADF (-4083)
|
||||
#endif
|
||||
|
||||
#if defined(EBUSY) && !defined(_WIN32)
|
||||
# define UV__EBUSY (-EBUSY)
|
||||
#else
|
||||
# define UV__EBUSY (-4082)
|
||||
#endif
|
||||
|
||||
#if defined(ECANCELED) && !defined(_WIN32)
|
||||
# define UV__ECANCELED (-ECANCELED)
|
||||
#else
|
||||
# define UV__ECANCELED (-4081)
|
||||
#endif
|
||||
|
||||
#if defined(ECHARSET) && !defined(_WIN32)
|
||||
# define UV__ECHARSET (-ECHARSET)
|
||||
#else
|
||||
# define UV__ECHARSET (-4080)
|
||||
#endif
|
||||
|
||||
#if defined(ECONNABORTED) && !defined(_WIN32)
|
||||
# define UV__ECONNABORTED (-ECONNABORTED)
|
||||
#else
|
||||
# define UV__ECONNABORTED (-4079)
|
||||
#endif
|
||||
|
||||
#if defined(ECONNREFUSED) && !defined(_WIN32)
|
||||
# define UV__ECONNREFUSED (-ECONNREFUSED)
|
||||
#else
|
||||
# define UV__ECONNREFUSED (-4078)
|
||||
#endif
|
||||
|
||||
#if defined(ECONNRESET) && !defined(_WIN32)
|
||||
# define UV__ECONNRESET (-ECONNRESET)
|
||||
#else
|
||||
# define UV__ECONNRESET (-4077)
|
||||
#endif
|
||||
|
||||
#if defined(EDESTADDRREQ) && !defined(_WIN32)
|
||||
# define UV__EDESTADDRREQ (-EDESTADDRREQ)
|
||||
#else
|
||||
# define UV__EDESTADDRREQ (-4076)
|
||||
#endif
|
||||
|
||||
#if defined(EEXIST) && !defined(_WIN32)
|
||||
# define UV__EEXIST (-EEXIST)
|
||||
#else
|
||||
# define UV__EEXIST (-4075)
|
||||
#endif
|
||||
|
||||
#if defined(EFAULT) && !defined(_WIN32)
|
||||
# define UV__EFAULT (-EFAULT)
|
||||
#else
|
||||
# define UV__EFAULT (-4074)
|
||||
#endif
|
||||
|
||||
#if defined(EHOSTUNREACH) && !defined(_WIN32)
|
||||
# define UV__EHOSTUNREACH (-EHOSTUNREACH)
|
||||
#else
|
||||
# define UV__EHOSTUNREACH (-4073)
|
||||
#endif
|
||||
|
||||
#if defined(EINTR) && !defined(_WIN32)
|
||||
# define UV__EINTR (-EINTR)
|
||||
#else
|
||||
# define UV__EINTR (-4072)
|
||||
#endif
|
||||
|
||||
#if defined(EINVAL) && !defined(_WIN32)
|
||||
# define UV__EINVAL (-EINVAL)
|
||||
#else
|
||||
# define UV__EINVAL (-4071)
|
||||
#endif
|
||||
|
||||
#if defined(EIO) && !defined(_WIN32)
|
||||
# define UV__EIO (-EIO)
|
||||
#else
|
||||
# define UV__EIO (-4070)
|
||||
#endif
|
||||
|
||||
#if defined(EISCONN) && !defined(_WIN32)
|
||||
# define UV__EISCONN (-EISCONN)
|
||||
#else
|
||||
# define UV__EISCONN (-4069)
|
||||
#endif
|
||||
|
||||
#if defined(EISDIR) && !defined(_WIN32)
|
||||
# define UV__EISDIR (-EISDIR)
|
||||
#else
|
||||
# define UV__EISDIR (-4068)
|
||||
#endif
|
||||
|
||||
#if defined(ELOOP) && !defined(_WIN32)
|
||||
# define UV__ELOOP (-ELOOP)
|
||||
#else
|
||||
# define UV__ELOOP (-4067)
|
||||
#endif
|
||||
|
||||
#if defined(EMFILE) && !defined(_WIN32)
|
||||
# define UV__EMFILE (-EMFILE)
|
||||
#else
|
||||
# define UV__EMFILE (-4066)
|
||||
#endif
|
||||
|
||||
#if defined(EMSGSIZE) && !defined(_WIN32)
|
||||
# define UV__EMSGSIZE (-EMSGSIZE)
|
||||
#else
|
||||
# define UV__EMSGSIZE (-4065)
|
||||
#endif
|
||||
|
||||
#if defined(ENAMETOOLONG) && !defined(_WIN32)
|
||||
# define UV__ENAMETOOLONG (-ENAMETOOLONG)
|
||||
#else
|
||||
# define UV__ENAMETOOLONG (-4064)
|
||||
#endif
|
||||
|
||||
#if defined(ENETDOWN) && !defined(_WIN32)
|
||||
# define UV__ENETDOWN (-ENETDOWN)
|
||||
#else
|
||||
# define UV__ENETDOWN (-4063)
|
||||
#endif
|
||||
|
||||
#if defined(ENETUNREACH) && !defined(_WIN32)
|
||||
# define UV__ENETUNREACH (-ENETUNREACH)
|
||||
#else
|
||||
# define UV__ENETUNREACH (-4062)
|
||||
#endif
|
||||
|
||||
#if defined(ENFILE) && !defined(_WIN32)
|
||||
# define UV__ENFILE (-ENFILE)
|
||||
#else
|
||||
# define UV__ENFILE (-4061)
|
||||
#endif
|
||||
|
||||
#if defined(ENOBUFS) && !defined(_WIN32)
|
||||
# define UV__ENOBUFS (-ENOBUFS)
|
||||
#else
|
||||
# define UV__ENOBUFS (-4060)
|
||||
#endif
|
||||
|
||||
#if defined(ENODEV) && !defined(_WIN32)
|
||||
# define UV__ENODEV (-ENODEV)
|
||||
#else
|
||||
# define UV__ENODEV (-4059)
|
||||
#endif
|
||||
|
||||
#if defined(ENOENT) && !defined(_WIN32)
|
||||
# define UV__ENOENT (-ENOENT)
|
||||
#else
|
||||
# define UV__ENOENT (-4058)
|
||||
#endif
|
||||
|
||||
#if defined(ENOMEM) && !defined(_WIN32)
|
||||
# define UV__ENOMEM (-ENOMEM)
|
||||
#else
|
||||
# define UV__ENOMEM (-4057)
|
||||
#endif
|
||||
|
||||
#if defined(ENONET) && !defined(_WIN32)
|
||||
# define UV__ENONET (-ENONET)
|
||||
#else
|
||||
# define UV__ENONET (-4056)
|
||||
#endif
|
||||
|
||||
#if defined(ENOSPC) && !defined(_WIN32)
|
||||
# define UV__ENOSPC (-ENOSPC)
|
||||
#else
|
||||
# define UV__ENOSPC (-4055)
|
||||
#endif
|
||||
|
||||
#if defined(ENOSYS) && !defined(_WIN32)
|
||||
# define UV__ENOSYS (-ENOSYS)
|
||||
#else
|
||||
# define UV__ENOSYS (-4054)
|
||||
#endif
|
||||
|
||||
#if defined(ENOTCONN) && !defined(_WIN32)
|
||||
# define UV__ENOTCONN (-ENOTCONN)
|
||||
#else
|
||||
# define UV__ENOTCONN (-4053)
|
||||
#endif
|
||||
|
||||
#if defined(ENOTDIR) && !defined(_WIN32)
|
||||
# define UV__ENOTDIR (-ENOTDIR)
|
||||
#else
|
||||
# define UV__ENOTDIR (-4052)
|
||||
#endif
|
||||
|
||||
#if defined(ENOTEMPTY) && !defined(_WIN32)
|
||||
# define UV__ENOTEMPTY (-ENOTEMPTY)
|
||||
#else
|
||||
# define UV__ENOTEMPTY (-4051)
|
||||
#endif
|
||||
|
||||
#if defined(ENOTSOCK) && !defined(_WIN32)
|
||||
# define UV__ENOTSOCK (-ENOTSOCK)
|
||||
#else
|
||||
# define UV__ENOTSOCK (-4050)
|
||||
#endif
|
||||
|
||||
#if defined(ENOTSUP) && !defined(_WIN32)
|
||||
# define UV__ENOTSUP (-ENOTSUP)
|
||||
#else
|
||||
# define UV__ENOTSUP (-4049)
|
||||
#endif
|
||||
|
||||
#if defined(EPERM) && !defined(_WIN32)
|
||||
# define UV__EPERM (-EPERM)
|
||||
#else
|
||||
# define UV__EPERM (-4048)
|
||||
#endif
|
||||
|
||||
#if defined(EPIPE) && !defined(_WIN32)
|
||||
# define UV__EPIPE (-EPIPE)
|
||||
#else
|
||||
# define UV__EPIPE (-4047)
|
||||
#endif
|
||||
|
||||
#if defined(EPROTO) && !defined(_WIN32)
|
||||
# define UV__EPROTO (-EPROTO)
|
||||
#else
|
||||
# define UV__EPROTO (-4046)
|
||||
#endif
|
||||
|
||||
#if defined(EPROTONOSUPPORT) && !defined(_WIN32)
|
||||
# define UV__EPROTONOSUPPORT (-EPROTONOSUPPORT)
|
||||
#else
|
||||
# define UV__EPROTONOSUPPORT (-4045)
|
||||
#endif
|
||||
|
||||
#if defined(EPROTOTYPE) && !defined(_WIN32)
|
||||
# define UV__EPROTOTYPE (-EPROTOTYPE)
|
||||
#else
|
||||
# define UV__EPROTOTYPE (-4044)
|
||||
#endif
|
||||
|
||||
#if defined(EROFS) && !defined(_WIN32)
|
||||
# define UV__EROFS (-EROFS)
|
||||
#else
|
||||
# define UV__EROFS (-4043)
|
||||
#endif
|
||||
|
||||
#if defined(ESHUTDOWN) && !defined(_WIN32)
|
||||
# define UV__ESHUTDOWN (-ESHUTDOWN)
|
||||
#else
|
||||
# define UV__ESHUTDOWN (-4042)
|
||||
#endif
|
||||
|
||||
#if defined(ESPIPE) && !defined(_WIN32)
|
||||
# define UV__ESPIPE (-ESPIPE)
|
||||
#else
|
||||
# define UV__ESPIPE (-4041)
|
||||
#endif
|
||||
|
||||
#if defined(ESRCH) && !defined(_WIN32)
|
||||
# define UV__ESRCH (-ESRCH)
|
||||
#else
|
||||
# define UV__ESRCH (-4040)
|
||||
#endif
|
||||
|
||||
#if defined(ETIMEDOUT) && !defined(_WIN32)
|
||||
# define UV__ETIMEDOUT (-ETIMEDOUT)
|
||||
#else
|
||||
# define UV__ETIMEDOUT (-4039)
|
||||
#endif
|
||||
|
||||
#if defined(ETXTBSY) && !defined(_WIN32)
|
||||
# define UV__ETXTBSY (-ETXTBSY)
|
||||
#else
|
||||
# define UV__ETXTBSY (-4038)
|
||||
#endif
|
||||
|
||||
#if defined(EXDEV) && !defined(_WIN32)
|
||||
# define UV__EXDEV (-EXDEV)
|
||||
#else
|
||||
# define UV__EXDEV (-4037)
|
||||
#endif
|
||||
|
||||
#if defined(EFBIG) && !defined(_WIN32)
|
||||
# define UV__EFBIG (-EFBIG)
|
||||
#else
|
||||
# define UV__EFBIG (-4036)
|
||||
#endif
|
||||
|
||||
#if defined(ENOPROTOOPT) && !defined(_WIN32)
|
||||
# define UV__ENOPROTOOPT (-ENOPROTOOPT)
|
||||
#else
|
||||
# define UV__ENOPROTOOPT (-4035)
|
||||
#endif
|
||||
|
||||
#if defined(ERANGE) && !defined(_WIN32)
|
||||
# define UV__ERANGE (-ERANGE)
|
||||
#else
|
||||
# define UV__ERANGE (-4034)
|
||||
#endif
|
||||
|
||||
#if defined(ENXIO) && !defined(_WIN32)
|
||||
# define UV__ENXIO (-ENXIO)
|
||||
#else
|
||||
# define UV__ENXIO (-4033)
|
||||
#endif
|
||||
|
||||
#if defined(EMLINK) && !defined(_WIN32)
|
||||
# define UV__EMLINK (-EMLINK)
|
||||
#else
|
||||
# define UV__EMLINK (-4032)
|
||||
#endif
|
||||
|
||||
/* EHOSTDOWN is not visible on BSD-like systems when _POSIX_C_SOURCE is
|
||||
* defined. Fortunately, its value is always 64 so it's possible albeit
|
||||
* icky to hard-code it.
|
||||
*/
|
||||
#if defined(EHOSTDOWN) && !defined(_WIN32)
|
||||
# define UV__EHOSTDOWN (-EHOSTDOWN)
|
||||
#elif defined(__APPLE__) || \
|
||||
defined(__DragonFly__) || \
|
||||
defined(__FreeBSD__) || \
|
||||
defined(__FreeBSD_kernel__) || \
|
||||
defined(__NetBSD__) || \
|
||||
defined(__OpenBSD__)
|
||||
# define UV__EHOSTDOWN (-64)
|
||||
#else
|
||||
# define UV__EHOSTDOWN (-4031)
|
||||
#endif
|
||||
|
||||
#endif /* UV_ERRNO_H_ */
|
||||
@@ -0,0 +1,34 @@
|
||||
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef UV_LINUX_H
|
||||
#define UV_LINUX_H
|
||||
|
||||
#define UV_PLATFORM_LOOP_FIELDS \
|
||||
uv__io_t inotify_read_watcher; \
|
||||
void* inotify_watchers; \
|
||||
int inotify_fd; \
|
||||
|
||||
#define UV_PLATFORM_FS_EVENT_FIELDS \
|
||||
void* watchers[2]; \
|
||||
int wd; \
|
||||
|
||||
#endif /* UV_LINUX_H */
|
||||
@@ -0,0 +1,30 @@
|
||||
/* Copyright libuv project contributors. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef UV_MVS_H
|
||||
#define UV_MVS_H
|
||||
|
||||
#define UV_PLATFORM_SEM_T int
|
||||
|
||||
#define UV_PLATFORM_LOOP_FIELDS \
|
||||
void* ep; \
|
||||
|
||||
#endif /* UV_MVS_H */
|
||||
Loaded 100 of 1030 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user