diff --git a/.rootkeys b/.rootkeys index 5c57fbab49..d8edf89b73 100644 --- a/.rootkeys +++ b/.rootkeys @@ -201,6 +201,8 @@ 3f6dc140C8tAeBfroAF24VrmCS4v_w tools/misc/miniterm/README 3f6dc142IHaf6XIcAYGmhV9nNSIHFQ tools/misc/miniterm/miniterm.c 40c9c469kT0H9COWzA4XzPBjWK0WsA tools/misc/netfix +4022a73cEKvrYe_DVZW2JlAxobg9wg tools/misc/nsplitd/Makefile +4022a73cKms4Oq030x2JBzUB426lAQ tools/misc/nsplitd/nsplitd.c 3f870808_8aFBAcZbWiWGdgrGQyIEw tools/misc/p4perf.h 3f5ef5a2ir1kVAthS14Dc5QIRCEFWg tools/misc/xen-clone 3f5ef5a2dTZP0nnsFoeq2jRf3mWDDg tools/misc/xen-clone.README @@ -239,6 +241,7 @@ 40c9c468xzANp6o2D_MeCYwNmOIUsQ tools/python/xen/xend/XendVnet.py 40c9c468x191zetrVlMnExfsQWHxIQ tools/python/xen/xend/__init__.py 40c9c468S2YnCEKmk4ey8XQIST7INg tools/python/xen/xend/encode.py +40e9808elkoRulOo1GxRTp5ulJGVNw tools/python/xen/xend/packing.py 40c9c468DCpMe542varOolW1Xc68ew tools/python/xen/xend/server/SrvBase.py 40c9c468IxQabrKJSWs0aEjl-27mRQ tools/python/xen/xend/server/SrvConsole.py 40c9c4689Io5bxfbYIfRiUvsiLX0EQ tools/python/xen/xend/server/SrvConsoleDir.py @@ -257,7 +260,6 @@ 40c9c469N2-b3GqpLHHHPZykJPLVvA tools/python/xen/xend/server/channel.py 40c9c469hJ_IlatRne-9QEa0-wlquw tools/python/xen/xend/server/console.py 40c9c469UcNJh_NuLU0ytorM0Lk5Ow tools/python/xen/xend/server/controller.py -40c9c469vHh-qLiiubdbKEQbJf18Zw tools/python/xen/xend/server/cstruct.py 40d83983OXjt-y3HjSCcuoPp9rzvmw tools/python/xen/xend/server/domain.py 40c9c469yrm31i60pGKslTi2Zgpotg tools/python/xen/xend/server/messages.py 40c9c46925x-Rjb0Cv2f1-l2jZrPYg tools/python/xen/xend/server/netif.py @@ -265,6 +267,7 @@ 40c9c469LNxLVizOUpOjEaTKKCm8Aw tools/python/xen/xend/sxp.py 40d05079aFRp6NQdo5wIh5Ly31c0cg tools/python/xen/xm/__init__.py 40cf2937gKQcATgXKGtNeWb1PDH5nA tools/python/xen/xm/create.py +40e41cd2w0I4En6qrJn4em8HkK_oxQ tools/python/xen/xm/help.py 40cf2937isyS250zyd0Q2GuEDoNXfQ tools/python/xen/xm/main.py 40cf2937PSslwBliN1g7ofDy2H_RhA tools/python/xen/xm/opts.py 40cf2937Z8WCNOnO2FcWdubvEAF9QQ tools/python/xen/xm/shutdown.py @@ -274,6 +277,32 @@ 403a3edbVpV2E_wq1zeEkJ_n4Uu2eg tools/xentrace/xentrace.c 403a3edblCUrzSj0mmKhO5HOPrOrSQ tools/xentrace/xentrace_format 4050c413NtuyIq5lsYJV4P7KIjujXw tools/xentrace/xentrace_format.1 +40e9808eHO3QprCFKg9l2JJzgt2voA tools/xfrd/Make.xfrd +40e9808epTR4zWrYjGUnaaynK20Q5A tools/xfrd/Makefile +40e9808eysqT4VNDlJFqsZB2rdg4Qw tools/xfrd/connection.c +40e9808eyXfJUi4E0C3WSgrEXqQ1sQ tools/xfrd/connection.h +40e9808eULGwffNOE4kBrAfZ9YAVMA tools/xfrd/debug.h +40e9808eyjiahG5uF6AMelNVujBzCg tools/xfrd/enum.c +40e9808eZpbdn9q2KSSMGCNvY_ZgpQ tools/xfrd/enum.h +40e9808easXCzzAZQodEfKAhgUXSPA tools/xfrd/hash_table.c +40e9808e94BNXIVVKBFHC3rnkvwtJg tools/xfrd/hash_table.h +40e9808epW9iHcLXuO3QfUfLzB7onw tools/xfrd/lexis.c +40e9808egccMhCizayQRGtpBA3L5MQ tools/xfrd/lexis.h +40e9808ePADCSKL1YgGCt2TbYPnYkw tools/xfrd/lzi_stream.c +40e9808eDNAdpF71o5teYb9DTT-PRw tools/xfrd/lzi_stream.h +40e9808eQxi0EzTcPJtosrzxEIjA-Q tools/xfrd/marshal.c +40e9808etg13xfRm0Lqd8vY-jHOoTg tools/xfrd/marshal.h +40e9808eCsmywryb036TdtRMJHDMmQ tools/xfrd/select.c +40e9808e99OcM547cKMTfmCVSoWVAw tools/xfrd/select.h +40e9808e5_PLdodqVOSx0b4T_f5aeg tools/xfrd/sxpr.c +40e9808e0O4sHZtkDv5hlSqjYcdQAQ tools/xfrd/sxpr.h +40e9808eF3NVldqRNS5IHM8gbFAvpw tools/xfrd/xdr.c +40e9808ezXzoRHm7pybXU69NtnjimA tools/xfrd/xdr.h +40e9808edpUtf4bJ8IbqClPJj_OvbA tools/xfrd/xen_domain.c +40e9808eHviFFIwdUKOA234uIeifjA tools/xfrd/xen_domain.h +40e9808eIFeV-MDCNyVTNt5NfMPKeQ tools/xfrd/xfrd.c +40e9808eGIbOoSNJRiwWK2C3mjGWaA tools/xfrd/xfrd.h +40e9808eHXvs_5eggj9McD_J90mhNw tools/xfrd/xfrdClient.py 3f72f1bdJPsV3JCnBqs9ddL9tr6D2g xen/COPYING 3ddb79bcbOVHh38VJzc97-JEGD4dJQ xen/Makefile 3ddb79bcWnTwYsQRWl_PaneJfa6p0w xen/Rules.mk @@ -285,8 +314,7 @@ 40e42bdbNu4MjI750THP_8J1S-Sa0g xen/arch/x86/boot/x86_64.S 3ddb79bcUrk2EIaM5VsT6wUudH1kkg xen/arch/x86/delay.c 40e34414WiQO4h2m3tcpaCPn7SyYyg xen/arch/x86/dom0_ops.c -3e32af9aRnYGl4GMOaDKp7JdfhOGhg xen/arch/x86/domain_page.c -3ddb79bcecupHj56ZbTa3B0FxDowMg xen/arch/x86/entry.S +3ddb79bc1_2bAt67x9MFCP4AZrQnvQ xen/arch/x86/domain.c 3ddb79bcY5zW7KhvI9gvfuPi3ZumEg xen/arch/x86/extable.c 3fe443fdDDb0Sw6NQBCk4GQapayfTA xen/arch/x86/flushtlb.c 3ddb79bcesE5E-lS4QhRhlqXxqj9cA xen/arch/x86/i387.c @@ -294,7 +322,6 @@ 3ddb79bcBit4xJXbwtX0kb1hh2uO1Q xen/arch/x86/idle0_task.c 3ddb79bcKIkRR0kqWaJhe5VUDkMdxg xen/arch/x86/io_apic.c 3ddb79bdqfIcjkz_h9Hvtp8Tk_19Zw xen/arch/x86/irq.c -3ddb79bcHwuCQDjBICDTSis52hWguw xen/arch/x86/mm.c 3ddb79bdS4UeWWXDH-FaBKqcpMFcnw xen/arch/x86/mpparse.c 3f12cff65EV3qOG2j37Qm0ShgvXGRw xen/arch/x86/nmi.c 3ddb79bdHe6_Uij4-glW91vInNtBYQ xen/arch/x86/pci-irq.c @@ -303,7 +330,6 @@ 3ddb79bdIKgipvGoqExEQ7jawfVowA xen/arch/x86/pci-x86.h 40a4dfced2dnSzbKgJFlD3chKHexjQ xen/arch/x86/pdb-linux.c 4022a73czgX7d-2zfF_cb33oVemApQ xen/arch/x86/pdb-stub.c -3ddb79bc1_2bAt67x9MFCP4AZrQnvQ xen/arch/x86/process.c 3ddb79bc7KxGCEJsgBnkDX7XjD_ZEQ xen/arch/x86/rwlock.c 3ddb79bcrD6Z_rUvSDgrvjyb4846Eg xen/arch/x86/setup.c 405b8599xI_PoEr3zZoJ2on-jdn7iw xen/arch/x86/shadow.c @@ -312,8 +338,14 @@ 3ddb79bc-Udq7ol-NX4q9XsYnN7A2Q xen/arch/x86/time.c 3ddb79bccYVzXZJyVaxuv5T42Z1Fsw xen/arch/x86/trampoline.S 3ddb79bcOftONV9h4QCxXOfiT0h91w xen/arch/x86/traps.c -3ddb79bc4nTpGQOe6_-MbyZzkhlhFQ xen/arch/x86/usercopy.c -3ddb79bcOMCu9-5mKpjIh5d0qqBDPg xen/arch/x86/xen.lds +3e32af9aRnYGl4GMOaDKp7JdfhOGhg xen/arch/x86/x86_32/domain_page.c +3ddb79bcecupHj56ZbTa3B0FxDowMg xen/arch/x86/x86_32/entry.S +3ddb79bcHwuCQDjBICDTSis52hWguw xen/arch/x86/x86_32/mm.c +3ddb79bc4nTpGQOe6_-MbyZzkhlhFQ xen/arch/x86/x86_32/usercopy.c +3ddb79bcOMCu9-5mKpjIh5d0qqBDPg xen/arch/x86/x86_32/xen.lds +40e96d3aLDI-nViMuYneD7VKYlZrVg xen/arch/x86/x86_64/entry.S +40e96d3ahBTZqbTViInnq0lM03vs7A xen/arch/x86/x86_64/usercopy.c +40e96d3akN3Hu_J5Bk-WXD8OGscrYQ xen/arch/x86/x86_64/xen.lds 3ddb79bdff-gj-jFGKjOejeHLqL8Lg xen/common/Makefile 3e397e66AyyD5fYraAySWuwi9uqSXg xen/common/ac_timer.c 4022a73c_BbDFd2YJ_NQYVvKX5Oz7w xen/common/debug-linux.c diff --git a/docs/HOWTOs/XenDebugger-HOWTO b/docs/HOWTOs/XenDebugger-HOWTO index 8d5dc69e69..1aba294abb 100644 --- a/docs/HOWTOs/XenDebugger-HOWTO +++ b/docs/HOWTOs/XenDebugger-HOWTO @@ -77,7 +77,7 @@ Serial Port Configuration one stream (without the high bit) is the console and one stream (with the high bit stripped) is the pdb communication. - See: xeno.bk/tools/nsplitd + See: xeno.bk/tools/misc/nsplitd nsplitd configuration --------------------- @@ -107,7 +107,7 @@ Serial Port Configuration characters received. You can connect to the nsplitd using - 'tools/xenctl/lib/console_client.py ' + 'tools/misc/xencons ' GDB 6.0 pdb has been tested with gdb 6.0. It should also work with diff --git a/linux-2.4.26-xen-sparse/arch/xen/drivers/blkif/backend/common.h b/linux-2.4.26-xen-sparse/arch/xen/drivers/blkif/backend/common.h index daf09a76f9..a9f2b87844 100644 --- a/linux-2.4.26-xen-sparse/arch/xen/drivers/blkif/backend/common.h +++ b/linux-2.4.26-xen-sparse/arch/xen/drivers/blkif/backend/common.h @@ -19,7 +19,7 @@ #define ASSERT(_p) \ if ( !(_p) ) { printk("Assertion '%s' failed, line %d, file %s", #_p , \ __LINE__, __FILE__); *(int*)0=0; } -#define DPRINTK(_f, _a...) printk("(file=%s, line=%d) " _f, \ +#define DPRINTK(_f, _a...) printk(KERN_ALERT "(file=%s, line=%d) " _f, \ __FILE__ , __LINE__ , ## _a ) #else #define ASSERT(_p) ((void)0) diff --git a/linux-2.4.26-xen-sparse/arch/xen/drivers/blkif/backend/interface.c b/linux-2.4.26-xen-sparse/arch/xen/drivers/blkif/backend/interface.c index 780d793c6c..16b37164ab 100644 --- a/linux-2.4.26-xen-sparse/arch/xen/drivers/blkif/backend/interface.c +++ b/linux-2.4.26-xen-sparse/arch/xen/drivers/blkif/backend/interface.c @@ -217,7 +217,7 @@ int blkif_disconnect(blkif_be_disconnect_t *disconnect, u8 rsp_id) blkif->status = DISCONNECTING; blkif->disconnect_rspid = rsp_id; wmb(); /* Let other CPUs see the status change. */ - free_irq(blkif->irq, NULL); + free_irq(blkif->irq, blkif); blkif_deschedule(blkif); blkif_put(blkif); } diff --git a/linux-2.4.26-xen-sparse/arch/xen/drivers/blkif/frontend/main.c b/linux-2.4.26-xen-sparse/arch/xen/drivers/blkif/frontend/main.c index fa662496ec..1216600eff 100644 --- a/linux-2.4.26-xen-sparse/arch/xen/drivers/blkif/frontend/main.c +++ b/linux-2.4.26-xen-sparse/arch/xen/drivers/blkif/frontend/main.c @@ -616,7 +616,7 @@ static void blkif_status_change(blkif_fe_interface_status_changed_t *status) printk(KERN_INFO "VBD driver recovery in progress\n"); - /* Prevent new requests being issued until we've fixed things up. */ + /* Prevent new requests being issued until we fix things up. */ spin_lock_irq(&io_request_lock); recovery = 1; blkif_state = BLKIF_STATE_DISCONNECTED; diff --git a/linux-2.4.26-xen-sparse/arch/xen/drivers/netif/backend/interface.c b/linux-2.4.26-xen-sparse/arch/xen/drivers/netif/backend/interface.c index 2c4b74e1ea..ff3bc7bbf7 100644 --- a/linux-2.4.26-xen-sparse/arch/xen/drivers/netif/backend/interface.c +++ b/linux-2.4.26-xen-sparse/arch/xen/drivers/netif/backend/interface.c @@ -266,7 +266,7 @@ int netif_disconnect(netif_be_disconnect_t *disconnect, u8 rsp_id) netif->disconnect_rspid = rsp_id; wmb(); /* Let other CPUs see the status change. */ netif_stop_queue(netif->dev); - free_irq(netif->irq, NULL); + free_irq(netif->irq, netif); netif_deschedule(netif); netif_put(netif); } diff --git a/linux-2.4.26-xen-sparse/arch/xen/kernel/process.c b/linux-2.4.26-xen-sparse/arch/xen/kernel/process.c index aa59c37790..6d80505743 100644 --- a/linux-2.4.26-xen-sparse/arch/xen/kernel/process.c +++ b/linux-2.4.26-xen-sparse/arch/xen/kernel/process.c @@ -115,7 +115,7 @@ void cpu_idle (void) } } -void machine_restart(char * __unused) +void machine_restart(char *__unused) { /* We really want to get pending console data out before we die. */ extern void xencons_force_flush(void); @@ -128,7 +128,11 @@ void machine_halt(void) /* We really want to get pending console data out before we die. */ extern void xencons_force_flush(void); xencons_force_flush(); - HYPERVISOR_shutdown(); + for ( ; ; ) /* loop without wasting cpu cycles */ + { + HYPERVISOR_shared_info->vcpu_data[0].evtchn_upcall_pending = 0; + HYPERVISOR_block(); + } } void machine_power_off(void) diff --git a/linux-2.4.26-xen-sparse/arch/xen/kernel/setup.c b/linux-2.4.26-xen-sparse/arch/xen/kernel/setup.c index b329632b78..42ef543546 100644 --- a/linux-2.4.26-xen-sparse/arch/xen/kernel/setup.c +++ b/linux-2.4.26-xen-sparse/arch/xen/kernel/setup.c @@ -8,6 +8,8 @@ * This file handles the architecture-dependent parts of initialization */ +#define __KERNEL_SYSCALLS__ +static int errno; #include #include #include @@ -30,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -1148,10 +1151,10 @@ void __init cpu_init (void) #include -/* Treat multiple suspend requests as a single one. */ -static int suspending; +/* Ignore multiple shutdown requests. */ +static int shutting_down = -1; -static void suspend_task(void *unused) +static void __do_suspend(void) { /* Hmmm... a cleaner interface to suspend/resume blkdevs would be nice. */ extern void blkdev_suspend(void); @@ -1220,7 +1223,7 @@ static void suspend_task(void *unused) HYPERVISOR_suspend(virt_to_machine(suspend_record) >> PAGE_SHIFT); - suspending = 0; + shutting_down = -1; memcpy(&start_info, &suspend_record->resume_info, sizeof(start_info)); @@ -1272,25 +1275,78 @@ static void suspend_task(void *unused) free_page((unsigned long)suspend_record); } -static struct tq_struct suspend_tq; - -static void shutdown_handler(ctrl_msg_t *msg, unsigned long id) +static int shutdown_process(void *__unused) { - if ( msg->subtype != CMSG_SHUTDOWN_SUSPEND ) + static char *envp[] = { "HOME=/", "TERM=linux", + "PATH=/sbin:/usr/sbin:/bin:/usr/bin", NULL }; + static char *restart_argv[] = { "/sbin/shutdown", "-r", "now", NULL }; + static char *poweroff_argv[] = { "/sbin/halt", "-p", NULL }; + + extern asmlinkage long sys_reboot(int magic1, int magic2, + unsigned int cmd, void *arg); + + daemonize(); + + switch ( shutting_down ) { - extern void ctrl_alt_del(void); - ctrl_if_send_response(msg); - ctrl_alt_del(); + case CMSG_SHUTDOWN_POWEROFF: + if ( execve("/sbin/halt", poweroff_argv, envp) < 0 ) + { + sys_reboot(LINUX_REBOOT_MAGIC1, + LINUX_REBOOT_MAGIC2, + LINUX_REBOOT_CMD_POWER_OFF, + NULL); + } + break; + + case CMSG_SHUTDOWN_REBOOT: + if ( execve("/sbin/shutdown", restart_argv, envp) < 0 ) + { + sys_reboot(LINUX_REBOOT_MAGIC1, + LINUX_REBOOT_MAGIC2, + LINUX_REBOOT_CMD_RESTART, + NULL); + } + break; } - else if ( !suspending ) + + return 0; +} + +static void __shutdown_handler(void *unused) +{ + int err; + + if ( shutting_down != CMSG_SHUTDOWN_SUSPEND ) { - suspending = 1; - suspend_tq.routine = suspend_task; - schedule_task(&suspend_tq); + err = kernel_thread(shutdown_process, NULL, CLONE_FS | CLONE_FILES); + if ( err < 0 ) + printk(KERN_ALERT "Error creating shutdown process!\n"); + else + shutting_down = -1; /* could try again */ } else { - printk(KERN_ALERT"Ignore queued suspend request\n"); + __do_suspend(); + } +} + +static void shutdown_handler(ctrl_msg_t *msg, unsigned long id) +{ + static struct tq_struct shutdown_tq; + + if ( (shutting_down == -1) && + ((msg->subtype == CMSG_SHUTDOWN_POWEROFF) || + (msg->subtype == CMSG_SHUTDOWN_REBOOT) || + (msg->subtype == CMSG_SHUTDOWN_SUSPEND)) ) + { + shutting_down = msg->subtype; + shutdown_tq.routine = __shutdown_handler; + schedule_task(&shutdown_tq); + } + else + { + printk("Ignore spurious shutdown request\n"); } ctrl_if_send_response(msg); diff --git a/tools/examples/xmdefaults b/tools/examples/xmdefaults index f7b2f53c21..640a7390a0 100644 --- a/tools/examples/xmdefaults +++ b/tools/examples/xmdefaults @@ -2,17 +2,23 @@ #============================================================================ # Python defaults setup for 'xm create'. # Edit this file to reflect the configuration of your system. -# This file expects the variable 'vmid' to be set. #============================================================================ -try: - vmid = int(vmid) # convert to integer -except: - raise ValueError, "Variable 'vmid' must be an integer" +# Define script variables here. +# xm_vars is defined automatically, use xm_vars.var() to define a variable. -if vmid <= 0: - raise ValueError, "Variable 'vmid' must be greater than 0" +def vmid_check(var, val): + val = int(val) + if val <= 0: + raise ValueError + return val + +xm_vars.var('vmid', + use="Virtual machine id. Integer greater than 0.", + check=vmid_check) +# This checks the script variables. +xm_vars.check() #---------------------------------------------------------------------------- # Kernel image file. @@ -87,6 +93,6 @@ extra = "4 VMID=%d usr=/dev/sda6" % vmid #---------------------------------------------------------------------------- # Set according to whether you want the domain restarted when it exits. # The default is False. -#restart = True +#autorestart = True #============================================================================ diff --git a/tools/examples/xmnetbsd b/tools/examples/xmnetbsd index 0563239464..4f920087b6 100644 --- a/tools/examples/xmnetbsd +++ b/tools/examples/xmnetbsd @@ -2,24 +2,23 @@ #============================================================================ # Python defaults setup for 'xm create'. # Edit this file to reflect the configuration of your system. -# This file expects the variable 'vmid' to be set. #============================================================================ -def config_usage (): - print >>sys.stderr,""" -The config file '%s' requires the following variable to be defined: - vmid -- Numeric identifier for the new domain, used to calculate - the VM's IP address and root partition. E.g. -Dvmid=1 -""" % config_file +# Define script variables here. +# xm_vars is defined automatically, use xm_vars.var() to define a variable. +def vmid_check(var, val): + val = int(val) + if val <= 0: + raise ValueError + return val + +xm_vars.var('vmid', + use="Virtual machine id. Integer greater than 0.", + check=vmid_check) -try: - vmid = int(vmid) # convert to integer -except: - raise ValueError, "Variable 'vmid' must be an integer" - -if vmid <= 0: - raise ValueError, "Variable 'vmid' must be greater than 0" +# This checks the script variables. +xm_vars.check() #---------------------------------------------------------------------------- # Kernel image file. @@ -97,6 +96,6 @@ extra = "4 VMID=%d bootdev=xennet0" % vmid #---------------------------------------------------------------------------- # Set according to whether you want the domain restarted when it exits. # The default is False. -#restart = True +#autorestart = True #============================================================================ diff --git a/tools/libxutil/Makefile b/tools/libxutil/Makefile index 8ba9eb3454..8be24daa92 100644 --- a/tools/libxutil/Makefile +++ b/tools/libxutil/Makefile @@ -8,7 +8,7 @@ LIB_SRCS += allocate.c LIB_SRCS += file_stream.c LIB_SRCS += gzip_stream.c LIB_SRCS += iostream.c -LIB_SRCS += sys_net.c +#LIB_SRCS += sys_net.c LIB_SRCS += sys_string.c LIB_OBJS := $(LIB_SRCS:.c=.o) diff --git a/tools/libxutil/allocate.h b/tools/libxutil/allocate.h index 08bc67b910..391b7be2bd 100644 --- a/tools/libxutil/allocate.h +++ b/tools/libxutil/allocate.h @@ -16,8 +16,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef _XEN_LIB_ALLOCATE_H_ -#define _XEN_LIB_ALLOCATE_H_ +#ifndef _XUTIL_ALLOCATE_H_ +#define _XUTIL_ALLOCATE_H_ /** Allocate memory for a given type, and cast. */ #define ALLOCATE(ctype) (ctype *)allocate(sizeof(ctype)) @@ -33,7 +33,7 @@ extern void memzero(void *p, int size); typedef void AllocateFailedFn(int size, int type); extern AllocateFailedFn *allocate_failed_fn; -#endif /* _XEN_LIB_ALLOCATE_H_ */ +#endif /* _XUTIL_ALLOCATE_H_ */ diff --git a/tools/libxutil/file_stream.h b/tools/libxutil/file_stream.h index 36a0f928b2..f717656c24 100644 --- a/tools/libxutil/file_stream.h +++ b/tools/libxutil/file_stream.h @@ -16,8 +16,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef _XEN_LIB_FILE_STREAM_H_ -#define _XEN_LIB_FILE_STREAM_H_ +#ifndef _XUTIL_FILE_STREAM_H_ +#define _XUTIL_FILE_STREAM_H_ #ifndef __KERNEL__ #include "iostream.h" @@ -32,4 +32,4 @@ extern IOStream get_stream_stdin(void); extern int file_stream_setvbuf(IOStream *io, char *buf, int mode, size_t size); #endif -#endif /* !_XEN_LIB_FILE_STREAM_H_ */ +#endif /* !_XUTIL_FILE_STREAM_H_ */ diff --git a/tools/libxutil/gzip_stream.c b/tools/libxutil/gzip_stream.c index af46023f9d..a933c5ff73 100644 --- a/tools/libxutil/gzip_stream.c +++ b/tools/libxutil/gzip_stream.c @@ -1,4 +1,3 @@ -/* $Id: gzip_stream.c,v 1.4 2003/09/30 15:22:53 mjw Exp $ */ /* * Copyright (C) 2003 Hewlett-Packard Company. * diff --git a/tools/libxutil/gzip_stream.h b/tools/libxutil/gzip_stream.h index cf76d25275..fd28e39575 100644 --- a/tools/libxutil/gzip_stream.h +++ b/tools/libxutil/gzip_stream.h @@ -1,4 +1,3 @@ -#/* $Id: gzip_stream.h,v 1.3 2003/09/30 15:22:53 mjw Exp $ */ /* * Copyright (C) 2003 Hewlett-Packard Company. * @@ -17,8 +16,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef _SP_GZIP_STREAM_H_ -#define _SP_GZIP_STREAM_H_ +#ifndef _XUTIL_GZIP_STREAM_H_ +#define _XUTIL_GZIP_STREAM_H_ #ifndef __KERNEL__ #include "iostream.h" @@ -28,4 +27,4 @@ extern IOStream *gzip_stream_new(gzFile *f); extern IOStream *gzip_stream_fopen(const char *file, const char *flags); extern IOStream *gzip_stream_fdopen(int fd, const char *flags); #endif -#endif /* !_SP_FILE_STREAM_H_ */ +#endif /* !_XUTIL_GZIP_STREAM_H_ */ diff --git a/tools/libxutil/kernel_stream.h b/tools/libxutil/kernel_stream.h index be370f2a45..0f18058d59 100644 --- a/tools/libxutil/kernel_stream.h +++ b/tools/libxutil/kernel_stream.h @@ -16,8 +16,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef _XEN_LIB_KERNEL_STREAM_H_ -#define _XEN_LIB_KERNEL_STREAM_H_ +#ifndef _XUTIL_KERNEL_STREAM_H_ +#define _XUTIL_KERNEL_STREAM_H_ #ifdef __KERNEL__ #include "iostream.h" @@ -26,4 +26,4 @@ extern IOStream get_stream_kernel(void); #define get_stream_stdout get_stream_kernel #endif /* __KERNEL__ */ -#endif /* !_XEN_LIB_KERNEL_STREAM_H_ */ +#endif /* !_XUTIL_KERNEL_STREAM_H_ */ diff --git a/tools/libxutil/string_stream.c b/tools/libxutil/string_stream.c index c3cf423d84..495238d74a 100644 --- a/tools/libxutil/string_stream.c +++ b/tools/libxutil/string_stream.c @@ -25,8 +25,6 @@ #include "string_stream.h" #include "allocate.h" -static int string_print(IOStream *io, const char *msg, va_list args); -static int string_getc(IOStream *io); static int string_error(IOStream *io); static int string_close(IOStream *io); static void string_free(IOStream *io); @@ -49,45 +47,6 @@ static inline StringData *get_string_data(IOStream *io){ return (StringData*)io->data; } -/** Read a character from a string stream. - * - * @param io string stream - * @return character read, IOSTREAM_EOF if no more input - */ -static int string_getc(IOStream *io){ - StringData *data = get_string_data(io); - int c = IOSTREAM_EOF; - char *s = data->in; - - if(s && s < data->end){ - c = (unsigned)*s; - data->in = s+1; - } - return c; -} - -/** Print to a string stream. - * Formats the data to an internal buffer and prints it. - * The formatted data must fit into the internal buffer. - * - * @param io string stream - * @param format print format - * @param args print arguments - * @return result of the print - */ -static int string_print(IOStream *io, const char *msg, va_list args){ - StringData *data = get_string_data(io); - int k = data->end - data->out; - int n = vsnprintf(data->out, k, (char*)msg, args); - if(n < 0 || n > k ){ - n = k; - IOStream_close(io); - } else { - data->out += n; - } - return n; -} - /** Test if a string stream has an error. * * @param io string stream @@ -118,7 +77,7 @@ static int string_close(IOStream *io){ */ static void string_free(IOStream *io){ StringData *data = get_string_data(io); - zero(data, sizeof(*data)); + memzero(data, sizeof(*data)); deallocate(data); } @@ -139,13 +98,13 @@ IOMethods *string_stream_get_methods(void){ */ void string_stream_init(IOStream *io, StringData *data, char *s, int n){ if(data && io){ - zero(data, sizeof(*data)); + memzero(data, sizeof(*data)); data->string = (char*)s; data->in = data->string; data->out = data->string; data->size = n; data->end = data->string + n; - zero(io, sizeof(*io)); + memzero(io, sizeof(*io)); io->methods = &string_methods; io->data = data; } diff --git a/tools/libxutil/string_stream.h b/tools/libxutil/string_stream.h index 36d764b265..b6cbc0f3df 100644 --- a/tools/libxutil/string_stream.h +++ b/tools/libxutil/string_stream.h @@ -1,4 +1,3 @@ -/* $Id: string_stream.h,v 1.1 2003/08/22 14:25:48 mjw Exp $ */ /* * Copyright (C) 2001, 2002 Hewlett-Packard Company. * @@ -17,8 +16,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef _SP_STRING_STREAM_H_ -#define _SP_STRING_STREAM_H_ +#ifndef _XUTIL_STRING_STREAM_H_ +#define _XUTIL_STRING_STREAM_H_ #include "iostream.h" @@ -43,4 +42,4 @@ extern IOMethods *string_stream_get_methods(void); extern IOStream *string_stream_new(char *s, int n); extern void string_stream_init(IOStream *stream, StringData *data, char *s, int n); -#endif /* !_SP_STRING_STREAM_H_ */ +#endif /* !_XUTIL_STRING_STREAM_H_ */ diff --git a/tools/libxutil/sys_net.h b/tools/libxutil/sys_net.h index da6c1e8fd5..61754940f2 100644 --- a/tools/libxutil/sys_net.h +++ b/tools/libxutil/sys_net.h @@ -16,8 +16,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef _XEN_LIB_SYS_NET_H_ -#define _XEN_LIB_SYS_NET_H_ +#ifndef _XUTIL_SYS_NET_H_ +#define _XUTIL_SYS_NET_H_ /** @file * * Replacement for standard network includes. @@ -72,7 +72,7 @@ extern int inet_aton(const char *address, struct in_addr *inp); extern char *mac_ntoa(const unsigned char *macaddr); extern int mac_aton(const char *addr, unsigned char *macaddr); -#endif /* !_SP_SYS_NET_H_ */ +#endif /* !_XUTIL_SYS_NET_H_ */ diff --git a/tools/libxutil/sys_string.h b/tools/libxutil/sys_string.h index f39935f669..ea60401168 100644 --- a/tools/libxutil/sys_string.h +++ b/tools/libxutil/sys_string.h @@ -16,8 +16,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef _XEN_LIB_SYS_STRING_H_ -#define _XEN_LIB_SYS_STRING_H_ +#ifndef _XUTIL_SYS_STRING_H_ +#define _XUTIL_SYS_STRING_H_ /** @file * Replacement for standard string includes. * Works in user or kernel code. @@ -88,4 +88,4 @@ static inline size_t strnlen(const char *s, size_t n){ extern int convert_atoul(const char *s, unsigned long *v); extern int path_concat(char *s, char *t, char **val); -#endif /* !_XEN_LIB_SYS_STRING_H_ */ +#endif /* !_XUTIL_SYS_STRING_H_ */ diff --git a/tools/misc/nsplitd/Makefile b/tools/misc/nsplitd/Makefile new file mode 100644 index 0000000000..c5c4c9ed40 --- /dev/null +++ b/tools/misc/nsplitd/Makefile @@ -0,0 +1,22 @@ + +CC = gcc +CFLAGS = -Wall -O3 +CFILES = $(wildcard *.c) + +HDRS = $(wildcard *.h) +OBJS = $(patsubst %.c,%.o,$(wildcard *.c)) + +TARGET = nsplitd + +all: $(TARGET) + +install: all + +clean: + $(RM) *.o $(TARGET) *~ + +$(TARGET): $(OBJS) + $(CC) $(CFLAGS) -o $@ $^ + +%.o: %.c $(HDRS) Makefile + $(CC) $(CFLAGS) -c -o $@ $< diff --git a/tools/misc/nsplitd/nsplitd.c b/tools/misc/nsplitd/nsplitd.c new file mode 100644 index 0000000000..48fbd65103 --- /dev/null +++ b/tools/misc/nsplitd/nsplitd.c @@ -0,0 +1,686 @@ +/* + * nsplitd.c + * --------- + * + * $Id: nsplitd.c,v 2.6 1998/09/17 14:28:37 sde1000 Exp $ + * + * Copyright (c) 1995, University of Cambridge Computer Laboratory, + * Copyright (c) 1995, Richard Black, All Rights Reserved. + * + * + * A complete re-implementation of DME's nsplitd for use from inetd + * + */ + +/* The basic stream comes in (via inetd) and we then conenct to + * somewhere else providing a loop-through service, except we offer + * two other ports for connection - one of which gets a second channel + * using the top bit to distinguish, and the other is a master control + * port (normally used for gdb) which gets complete exclusive access + * for its duration. + * + * Originally designed for multiplexing a xwcons/telnet with a gdb + * post-mortem debugging session. + * + * Here is a picture: + * + * port0 (from inetd) + * 8-bit connection / + * made by us <----> nsplitd <-----gdbport (default port0+2) + * to host:port/tcp |\ + * | port1 (default port0+1) + * \ + * control (default port0+3) + * + * If port1 is explicitly disabled (through a command-line option) then + * port0 becomes 8-bit clean. + */ + +/* + * N.B.: We do NOT support 8 bit stdin/stdout usage on a + * /dev/... because to do that right involves much messing with ioctl + * and TIOC... etc. If you want to do that sort of thing then the + * right way to do it is to chain this onto wconsd (which does know + * about and understand all the ioctl and TIOC grief). + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef FALSE +#define FALSE 0 +#endif +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef LOG_DAEMON +#define LOG_DAEMON 0 +#endif + +#define DB(x) /* ((x), fflush(stderr)) */ + +extern char *optarg; + +extern int optind, opterr, optopt; + +static char *prog_name; + +static void usage(void) +{ + fprintf(stderr, "This program (%s) should be run via inetd (tcp)\n\n", + prog_name); + fprintf(stderr, "usage: %s [-h][-g]" + "[-c][-8] host:service\n", + prog_name); + exit(1); +} + +static void fault(char *format, ...) +{ + va_list ap; + char logbuf[1024]; + + va_start(ap, format); + fprintf(stderr, "%s: ", prog_name); + vfprintf(stderr, format, ap); + fflush(stderr); + va_end(ap); + + /* XXX This is a bit dubious, but there is no vsyslog */ + va_start(ap, format); + vsprintf(logbuf, format, ap); + syslog(LOG_ERR, logbuf); + va_end(ap); + exit(1); +} + +static int getservice(char *name, unsigned short *port) +{ + struct servent *se; + + if (!name) return -1; + + if (isdigit(name[0])) + *port = atoi(name); + else + { + if (!(se = getservbyname(name, "tcp"))) + return -1; + *port = ntohs(se->s_port); + } + return 0; +} + +/* + * connect_host: connect to ("name", "port") + */ +static int connect_host (char *name, unsigned int port) +{ + int fd; + struct hostent *hostent; + struct sockaddr_in sin; + int on; + + if ((fd = socket (AF_INET, SOCK_STREAM, 0)) < 0) + fault("socket"); + + if (!(hostent = gethostbyname(name))) + fault("gethostbyname: %s: %s\n", name, strerror(errno)); + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = htons (port); + memcpy(&sin.sin_addr.s_addr, hostent->h_addr, sizeof(struct in_addr)); + + if (connect(fd, (struct sockaddr *) &sin, sizeof (sin)) < 0) + fault("connect: %s:%u: %s\n", name, port, strerror(errno)); + + on = 1; + if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof (on)) < 0) + syslog(LOG_WARNING, "setsockopt (TCP_NODELAY): %m"); + + on = 1; + if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0) + syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m"); + + return fd; +} + +/* + * open a tcp socket and start listening for connections on it + */ +static int startlistening(unsigned short port) +{ + int fd, on; + struct sockaddr_in sin; + + if ((fd = socket (AF_INET, SOCK_STREAM, 0)) < 0) + fault("socket"); + + on = 1; + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) < 0) + syslog(LOG_WARNING, "setsockopt (SO_REUSEADDR): %m"); + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = htons (port); + sin.sin_addr.s_addr = INADDR_ANY; + if (bind(fd, &sin, sizeof(sin)) < 0) + fault("bind: %u: %s\n", port, strerror(errno)); + + if (listen(fd, 1) < 0) + fault("listen: %s\n", strerror(errno)); + + return fd; +} + +static void noblock(int fd) +{ + int on=1; + + if (ioctl(fd, FIONBIO, &on) < 0) + fault("ioctl: FIONBIO: %s\n", strerror(errno)); +} + + +/* You might not believe this, but fd_sets don't have to be a 32-bit + * integer. In particular, in glibc2 it is an array of unsigned + * longs. Hence, this hacked up FD_SET_rjb() that works out if it + * would have been a nop. */ +#define FD_SET_rjb(fd, setp) \ +do { \ + if ((fd) != 32) \ + FD_SET((fd), (setp)); \ +} while(0) + +#define FD_ISSET_rjb(fd, setp) (((fd) != 32)? FD_ISSET((fd), (setp)) : 0) + +#define MAXSIZE 256 + +/* ----------------------------------------------------------------- + * The main bit of the algorithm. Note we use 32 to mean not connected + * because this gives us 1<<32 == 0. We could have done this one + * character at a time, but that would have been very inefficient and + * not the unix way. */ +static int debug; + +static void doit(int actl, int acto, int lish, int lisg, int lisc) +{ + int acth, actg, actc; + int gdbmode = FALSE; + char gibuf[MAXSIZE], oibuf[MAXSIZE]; + char libuf[MAXSIZE], lobuf[MAXSIZE]; + char hibuf[MAXSIZE], hobuf[MAXSIZE]; + char ctlbuf[MAXSIZE]; + fd_set rdfs, wrfs, exfs; + int gicc, oicc, licc, locc, hicc, hocc, ctlcc; + char *giptr, *oiptr, *liptr, *loptr, *hiptr, *hoptr; + int rc, fromlen; + struct sockaddr_in from; + + gicc = oicc = licc = locc = hicc = hocc = ctlcc = 0; + acth = actg = actc = 32; /* XXX yummy */ + + noblock(actl); + noblock(acto); + + for(;;) + { + FD_ZERO(&rdfs); + FD_ZERO(&wrfs); + FD_ZERO(&exfs); + + /* always take input from the control port (if it's connected) */ + FD_SET_rjb(actc, &rdfs); + + if (gdbmode) + { + if (oicc) + FD_SET_rjb(actg, &wrfs); + else + FD_SET_rjb(acto, &rdfs); + + if (gicc) + FD_SET_rjb(acto, &wrfs); + else + FD_SET_rjb(actg, &rdfs); + } + else + { + /* There is no such thing as oibuf because its been split into + * lobuf and hobuf + */ + if (locc || hocc) + { + if (locc) + FD_SET_rjb(actl, &wrfs); + if (hocc) + FD_SET_rjb(acth, &wrfs); + } + else + FD_SET_rjb(acto, &rdfs); + + if (licc) + FD_SET_rjb(acto, &wrfs); + else + FD_SET_rjb(actl, &rdfs); + + if (hicc) + FD_SET_rjb(acto, &wrfs); + else + FD_SET_rjb(acth, &rdfs); + } + + if (acth == 32 && lish>=0) FD_SET_rjb(lish, &rdfs); + if (actg == 32) FD_SET_rjb(lisg, &rdfs); + if (actc == 32) FD_SET_rjb(lisc, &rdfs); + + /* now make exfs the union of the read and write fd sets, plus + * "actl" */ + { + int i; + exfs = rdfs; + for(i=0; i<32; i++) /* XXX we only copy fd numbers up to 31 */ + if (FD_ISSET(i, &wrfs)) + FD_SET_rjb(i, &exfs); + FD_SET_rjb(actl, &exfs); + } + + /* XXX AND: can't print something of type fd_set as %x - it + * might be an array */ + DB(fprintf(stderr, "%s: before select: %08x %08x %08x\n", + prog_name, rdfs, wrfs, exfs)); + + if (select(32, &rdfs, &wrfs, &exfs, NULL) < 0) + fault("select: %s\n", strerror(errno)); + + DB(fprintf(stderr, "%s: after select: %08x %08x %08x\n", + prog_name, rdfs, wrfs, exfs)); + + /* XXX it appears that a non-blocking socket may not show up + * correctly in exfs but instead goes readable with no data in + * it. Thus we check for zero and goto the appropriate close + * method. */ + + /* Deal with exceptions */ + if (FD_ISSET_rjb(actg, &exfs)) + { + exfs_actg: + close(actg); + gdbmode = FALSE; + oicc = 0; + oiptr = oibuf; + actg = 32; + continue; /* because assumptions changed */ + } + if (FD_ISSET_rjb(acth, &exfs)) + { + exfs_acth: + close(acth); + hicc = hocc = 0; + hiptr = hibuf; + hoptr = hibuf; + acth = 32; + continue; /* because assumptions changed */ + } + if (FD_ISSET_rjb(actl, &exfs) || + FD_ISSET_rjb(acto, &exfs)) + { + exfs_actl: + exfs_acto: + /* Thats all folks ... */ + break; + } + if (FD_ISSET_rjb(actc, &exfs)) + { + exfs_ctl: + close(actc); + actc = 32; + ctlcc = 0; + continue; + } + + /* Deal with reading */ + if (FD_ISSET_rjb(acto, &rdfs)) + { + if ((oicc = read(acto, oiptr = oibuf, MAXSIZE)) < 0) + fault("read acto: %d: %s\n", oicc, strerror(errno)); + if (!oicc) goto exfs_acto; + + if (!gdbmode) + { + int t; + + assert((locc == 0) && (hocc == 0)); + loptr = lobuf; + hoptr = hobuf; + + if (lish>=0) { + for(t=0; t=0 && (FD_ISSET_rjb(lish, &rdfs))) + { + fromlen = sizeof(from); + if ((acth = accept(lish, &from, &fromlen)) < 0) + { + syslog(LOG_WARNING, "accept: %m"); + acth = 32; + } + else + { + noblock(acth); + hicc = hocc = 0; + syslog(LOG_INFO, "highbit client peer is %s:%u\n", + inet_ntoa(from.sin_addr), ntohs(from.sin_port)); + } + } + + if ((actg == 32) && (FD_ISSET_rjb(lisg, &rdfs))) + { + fromlen = sizeof(from); + if ((actg = accept(lisg, &from, &fromlen)) < 0) + { + syslog(LOG_WARNING, "accept: %m"); + actg = 32; + } + else + { + noblock(actg); + gicc = 0; + gdbmode = TRUE; + syslog(LOG_INFO, "gdb client peer is %s:%u\n", + inet_ntoa(from.sin_addr), ntohs(from.sin_port)); + } + } + + if ((actc == 32) && (FD_ISSET_rjb(lisc, &rdfs))) + { + fromlen = sizeof(from); + if ((actc = accept(lisc, &from, &fromlen)) < 0) + { + syslog(LOG_WARNING, "accept (ctl): %m"); + actc = 32; + } + else + { + noblock(actc); + syslog(LOG_INFO, "ctl client peer is %s:%u\n", + inet_ntoa(from.sin_addr), ntohs(from.sin_port)); + } + } + + /* Back to top of loop */ + } + + /* We are bailing because one of the primary connections has gone + * away. We close these all explicitly here because that way the + * timeout on reusing the port numbers is smnaller. */ + + close(acth); + close(actg); + /* XXX AND: why are we closing all these "character counts" ?? */ + close(gicc); + close(oicc); + close(licc); + close(locc); + close(hicc); + close(hocc); +} + +/* + * ------------------------------------------------------------ + */ +int main(int argc, char **argv) +{ + /* In general, suffix "l" is low channel, "h" is high channel, "g" + * is gdb channel, "c" is control channel and "o" is output channel. + */ + struct sockaddr_in from; + int infd = 0, outfd; + unsigned short portl, porth, portg, portc, porto; + int on = 1, c; + char *outname, *outservice; + int fromlen; + int lish, lisg, lisc; +#if 0 + FILE *newerr; +#endif /* 0 */ + + prog_name = argv[0]; + + if (isatty(infd)) + usage(); + + /* Here, then not just a simple idiot. */ + + signal(SIGPIPE, SIG_IGN); + + openlog(prog_name, LOG_PID, LOG_DAEMON); + + fromlen = sizeof(from); + if (getsockname(infd, &from, &fromlen) < 0) + fault("getsockname: %s", strerror(errno)); + if ((fromlen != sizeof(from)) || (from.sin_family != AF_INET)) + fault("not an inet socket (family=%d)\n", from.sin_family); + + portl = ntohs(from.sin_port); + porth = portl+1; + portg = porth+1; + portc = portg+1; + + fromlen = sizeof(from); + if (getpeername(infd, &from, &fromlen) < 0) + fault("getpeername: %s", strerror(errno)); + if ((fromlen != sizeof(from)) || (from.sin_family != AF_INET)) + fault("not an inet socket (family=%d)\n", from.sin_family); + + syslog(LOG_INFO, "on port %u peer is %s:%u\n", portl, + inet_ntoa(from.sin_addr), ntohs(from.sin_port)); + + if (setsockopt(infd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0) + syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m"); + + /* from here on, we map stderr to output on the connection so we can + * report errors to the remote user. + */ +#if 0 + if (!(newerr = fdopen(infd, "w"))) + syslog(LOG_WARNING, "fdopen: %m"); + else + *stderr = *newerr; +#endif + + while((c = getopt(argc, argv, "d8h:g:c:")) != EOF) + { + switch(c) + { + case 'd': + debug++; + break; + + case 'h': + /* high bit port */ + if (getservice(optarg, &porth) < 0) + fault("getservice failed (high port '%s')\n", optarg); + break; + + case 'g': + /* gdb port */ + if (getservice(optarg, &portg) < 0) + fault("getservice failed (gdb port '%s')\n", optarg); + break; + + case 'c': + /* control port */ + if (getservice(optarg, &portc) < 0) + fault("getservice failed (control port '%s')\n", optarg); + break; + + case '8': + /* 8-bit clean; no high port */ + porth=0; + break; + + default: + fault("bad argument list!\n"); + } + } + + if (argc != optind + 1) + fault("unparsed arguments (%d!=%d)\n", argc, optind+1); + + outname = argv[optind]; + if (!(outservice = strchr(outname, ':'))) + fault("output arg '%s' doesn't contain ':'\n", outname); + *outservice++ = 0; + if (getservice(outservice, &porto) < 0) + fault("getservice failed (output port '%s')\n", outservice); + + /* Time to start the sockets */ + + if (porth) { + lish = startlistening(porth); + } else { + lish = -1; + } + lisg = startlistening(portg); + lisc = startlistening(portc); + + outfd = connect_host(outname, porto); + + doit(infd, outfd, lish, lisg, lisc); + + syslog(LOG_INFO, "terminating normally\n"); + + fclose(stderr); + + closelog(); + exit(0); +} + +/* End $Id: nsplitd.c,v 2.6 1998/09/17 14:28:37 sde1000 Exp $ */ diff --git a/tools/python/xen/util/ip.py b/tools/python/xen/util/ip.py index 8396e0d014..39eb01f944 100644 --- a/tools/python/xen/util/ip.py +++ b/tools/python/xen/util/ip.py @@ -3,7 +3,7 @@ import re import socket import struct -def readlines(fd): +def _readlines(fd): """Version of readlines safe against EINTR. """ import errno @@ -21,7 +21,7 @@ def readlines(fd): lines.append(line) return lines -def readline(fd): +def _readline(fd): """Version of readline safe against EINTR. """ while 1: @@ -42,11 +42,14 @@ as it may have been moved onto the bridge. NBE_BRIDGE = 'nbe-br' def get_current_ipaddr(dev='eth0'): - """Return a string containing the primary IP address for the given - network interface (default 'eth0'). + """Get the primary IP address for the given network interface. + + dev network interface (default eth0) + + returns interface address as a string """ fd = os.popen( '/sbin/ifconfig ' + dev + ' 2>/dev/null' ) - lines = readlines(fd) + lines = _readlines(fd) for line in lines: m = re.search( '^\s+inet addr:([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+).*', line ) @@ -57,11 +60,14 @@ def get_current_ipaddr(dev='eth0'): return None def get_current_ipmask(dev='eth0'): - """Return a string containing the primary IP netmask for the given - network interface (default 'eth0'). + """Get the primary IP netmask for a network interface. + + dev network interface (default eth0) + + returns interface netmask as a string """ fd = os.popen( '/sbin/ifconfig ' + dev + ' 2>/dev/null' ) - lines = readlines(fd) + lines = _readlines(fd) for line in lines: m = re.search( '^.+Mask:([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+).*', line ) @@ -72,11 +78,14 @@ def get_current_ipmask(dev='eth0'): return None def get_current_ipgw(dev='eth0'): - """Return a string containing the IP gateway for the given - network interface (default 'eth0'). + """Get the IP gateway for a network interface. + + dev network interface (default eth0) + + returns gateway address as a string """ fd = os.popen( '/sbin/route -n' ) - lines = readlines(fd) + lines = _readlines(fd) for line in lines: m = re.search( '^\S+\s+([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)' + '\s+\S+\s+\S*G.*' + dev + '.*', line ) @@ -88,24 +97,46 @@ def get_current_ipgw(dev='eth0'): def inet_aton(addr): """Convert an IP addr in IPv4 dot notation into an int. + + addr IP address as a string + + returns integer """ b = socket.inet_aton(addr) return struct.unpack('!I', b)[0] def inet_ntoa(n): """Convert an int into an IP addr in IPv4 dot notation. + + n IP address + + returns string """ b = struct.pack('!I', n) return socket.inet_ntoa(b) def add_offset_to_ip(addr, offset): """Add a numerical offset to an IP addr in IPv4 dot notation. + + addr IP address + offset offset to add + + returns new address """ n = inet_aton(addr) n += offset return inet_ntoa(n) def check_subnet( ip, network, netmask ): + """Check if an IP address is in the subnet defined by + a network address and mask'. + + ip IP adress + network network address + netmask network mask + + returns 1 if it is in the subnet, 0 if not + """ n_ip = inet_aton(ip) n_net = inet_aton(network) n_mask = inet_aton(netmask) diff --git a/tools/python/xen/xend/XendClient.py b/tools/python/xen/xend/XendClient.py index 13dc3dbb1e..b5d57b7174 100644 --- a/tools/python/xen/xend/XendClient.py +++ b/tools/python/xen/xend/XendClient.py @@ -1,6 +1,11 @@ # Copyright (C) 2004 Mike Wray """Client API for the HTTP interface on xend. Callable as a script - see main(). + +This API is the 'control-plane' for xend. +The 'data-plane' is done separately. For example, consoles +are accessed via sockets on xend, but the list of consoles +is accessible via this API. """ import sys import httplib @@ -27,6 +32,8 @@ class Foo(httplib.HTTPResponse): def sxprio(sxpr): + """Convert an sxpr to a string. + """ io = StringIO() sxp.show(sxpr, out=io) print >> io @@ -80,6 +87,12 @@ def eventurl(location, root, id=''): return urljoin(location, root, 'event/', id) def xend_request(url, method, data=None): + """Make a request to xend. + + url xend request url + method http method: POST or GET + data request argument data (dict) + """ urlinfo = urlparse.urlparse(url) (uproto, ulocation, upath, uparam, uquery, ufrag) = urlinfo if DEBUG: print url, urlinfo @@ -122,20 +135,37 @@ def xend_request(url, method, data=None): return val def xend_get(url, args=None): + """Make a xend request using GET. + Requests using GET are 'safe' and may be repeated without + nasty side-effects. + """ return xend_request(url, "GET", args) def xend_call(url, data): + """Make xend request using POST. + Requests using POST potentially cause side-effects and should + not be repeated unless it really is wanted to do the side + effect again. + """ return xend_request(url, "POST", data) class Xend: + """Default location of the xend server.""" SRV_DEFAULT = "localhost:8000" + + """Default path to the xend root on the server.""" ROOT_DEFAULT = "/xend/" def __init__(self, srv=None, root=None): self.bind(srv, root) def bind(self, srv=None, root=None): + """Bind to a given server. + + srv server location (host:port) + root server xend root path + """ if srv is None: srv = self.SRV_DEFAULT if root is None: root = self.ROOT_DEFAULT if not root.endswith('/'): root += '/' @@ -195,9 +225,10 @@ class Xend: return xend_call(self.domainurl(id), {'op' : 'pause'}) - def xend_domain_shutdown(self, id): + def xend_domain_shutdown(self, id, reason): return xend_call(self.domainurl(id), - {'op' : 'shutdown'}) + {'op' : 'shutdown', + 'reason' : reason }) def xend_domain_destroy(self, id): return xend_call(self.domainurl(id), diff --git a/tools/python/xen/xend/XendDomain.py b/tools/python/xen/xend/XendDomain.py index 328450a6b5..8b9db8101c 100644 --- a/tools/python/xen/xend/XendDomain.py +++ b/tools/python/xen/xend/XendDomain.py @@ -5,8 +5,11 @@ Needs to be persistent for one uptime. """ import sys +import traceback from twisted.internet import defer +defer.Deferred.debug = 1 +from twisted.internet import reactor import xen.lowlevel.xc; xc = xen.lowlevel.xc.new() @@ -28,10 +31,19 @@ __all__ = [ "XendDomain" ] class XendDomain: """Index of all domains. Singleton. """ - + + """Path to domain database.""" dbpath = "domain" + + """Table of domain info indexed by domain id.""" domain = {} + """Table of configs for domain restart, indexed by domain id.""" + restarts = {} + + """Table of delayed calls.""" + schedule = {} + def __init__(self): self.xconsole = XendConsole.instance() # Table of domain info indexed by domain id. @@ -49,6 +61,53 @@ class XendDomain: print 'XendDomain> virq', val self.reap() + def schedule_later(self, _delay, _name, _fn, *args): + """Schedule a function to be called later (if not already scheduled). + + _delay delay in seconds + _name schedule name + _fn function + args arguments + """ + if self.schedule.get(_name): return + self.schedule[_name] = reactor.callLater(_delay, _fn, *args) + + def schedule_cancel(self, name): + """Cancel a scheduled function call. + + name schedule name to cancel + """ + callid = self.schedule.get(name) + if not callid: + return + if callid.active(): + callid.cancel() + del self.schedule[name] + + def reap_schedule(self, delay=0): + """Schedule reap to be called later. + + delay delay in seconds + """ + self.schedule_later(delay, 'reap', self.reap) + + def reap_cancel(self): + """Cancel any scheduled reap. + """ + self.schedule_cancel('reap') + + def refresh_schedule(self, delay=0): + """Schedule refresh to be called later. + + delay delay in seconds + """ + self.schedule_later(delay, 'refresh', self.refresh) + + def refresh_cancel(self): + """Cancel any scheduled refresh. + """ + self.schedule_cancel('refresh') + def rm_all(self): """Remove all domain info. Used after reboot. """ @@ -145,6 +204,7 @@ class XendDomain: """Look for domains that have crashed or stopped. Tidy them up. """ + self.reap_cancel() print 'XendDomain>reap>' domlist = xc.domain_getinfo() casualties = [] @@ -158,12 +218,14 @@ class XendDomain: for d in casualties: id = str(d['dom']) print 'XendDomain>reap> died id=', id, d - self.domain_destroy(id, refresh=0) + self.final_domain_destroy(id) print 'XendDomain>reap<' def refresh(self): """Refresh domain list from Xen. """ + self.refresh_cancel() + print 'XendDomain>refresh>' domlist = xc.domain_getinfo() # Index the domlist by id. # Add entries for any domains we don't know about. @@ -184,7 +246,7 @@ class XendDomain: d.update(dominfo) else: self._delete_domain(d.id) - self.reap() + self.reap_schedule(1) def refresh_domain(self, id): """Refresh information for a single domain. @@ -267,21 +329,70 @@ class XendDomain: """Shutdown domain (nicely). id domain id - reason shutdown type: poweroff, reboot, halt + reason shutdown type: poweroff, reboot, suspend, halt """ dom = int(id) if dom <= 0: return 0 + self.domain_restart_schedule(id, reason) eserver.inject('xend.domain.shutdown', [id, reason]) + if reason == 'halt': + reason = 'poweroff' val = xend.domain_shutdown(dom, reason) - self.refresh() + self.refresh_schedule() return val - - def domain_destroy(self, id, refresh=1): - """Terminate domain immediately. + + def domain_restart_schedule(self, id, reason): + """Schedule a restart for a domain if it needs one. + + id domain id + reason shutdown reason + """ + if id in self.restarts: + # Don't schedule if already there. + return + restart = 0 + if reason in ['poweroff', 'reboot']: + dominfo = self.domain.get(id) + if dominfo and (dominfo.autorestart or reason == 'reboot'): + restart = 1 + # Clear autorestart flag to avoid multiple restarts. + dominfo.autorestart = 0 + + if restart: + self.restarts[id] = dominfo.config + + def domain_restart_cancel(self, id): + """Cancel any restart scheduled for a domain. + + id domain id + """ + dominfo = self.domain.get(id) + if dominfo: + dominfo.autorestart = 0 + if id in self.restarts: + del self.restarts[id] + + def domain_restarts(self): + """Execute any scheduled domain restarts for domains that have gone. + """ + for id in self.restarts.keys(): + if id in self.domain: + # Don't execute restart for domains still running. + continue + config = self.restarts[id] + # Remove it from the restarts. + del self.restarts[id] + try: + self.domain_create(config) + except: + print >>sys.stderr, "XendDomain> Exception restarting domain" + traceback.print_exc(sys.stderr) + + def final_domain_destroy(self, id): + """Final destruction of a domain.. id domain id - refresh send a domain destroy event if true """ dom = int(id) if dom <= 0: @@ -292,15 +403,26 @@ class XendDomain: val = dominfo.destroy() else: val = xc.domain_destroy(dom=dom) - if refresh: self.refresh() return val + def domain_destroy(self, id): + """Terminate domain immediately. + Camcels any restart for the domain. + + id domain id + """ + self.domain_restart_cancel(id) + val = self.final_domain_destroy(id) + self.refresh_schedule() + return val + def domain_migrate(self, id, dst): """Start domain migration. id domain id """ # Need a cancel too? + # Don't forget to cancel restart for it. pass def domain_save(self, id, dst, progress=0): diff --git a/tools/python/xen/xend/XendDomainInfo.py b/tools/python/xen/xend/XendDomainInfo.py index 869fc28e07..377e7bb61b 100644 --- a/tools/python/xen/xend/XendDomainInfo.py +++ b/tools/python/xen/xend/XendDomainInfo.py @@ -15,6 +15,7 @@ import sys import os from twisted.internet import defer +defer.Deferred.debug = 1 import xen.lowlevel.xc; xc = xen.lowlevel.xc.new() import xen.util.ip @@ -291,6 +292,9 @@ def vm_restore(src, progress=0): if dom < 0: raise VmError('restore failed') vmconfig = sxp.from_string(d['vmconfig']) + if not vmconfig: + raise VmError('bad vmconfig s-expression') + vmconfig = vmconfig[0] vm.config = sxp.child_value(vmconfig, 'config') deferred = vm.dom_configure(dom) def vifs_cb(val, vm): @@ -362,6 +366,8 @@ class XendDomainInfo: self.state = self.STATE_OK #todo: set to migrate info if migrating self.migrate = None + #Whether to auto-restart + self.autorestart = 0 def setdom(self, dom): self.dom = int(dom) @@ -417,7 +423,9 @@ class XendDomainInfo: self.config = config try: self.name = sxp.child_value(config, 'name') - self.memory = int(sxp.child_value(config, 'memory', '128')) + self.memory = int(sxp.child_value(config, 'memory') or '128') + if sxp.child(config, 'autorestart'): + self.autorestart = 1 self.configure_backends() image = sxp.child_value(config, 'image') image_name = sxp.name(image) @@ -426,14 +434,18 @@ class XendDomainInfo: raise VmError('unknown image type: ' + image_name) image_handler(self, image) deferred = self.configure() + def cbok(x): + print 'vm_create> cbok', x + return x + def cberr(err): + self.destroy() + return err + deferred.addCallback(cbok) + deferred.addErrback(cberr) except StandardError, ex: # Catch errors, cleanup and re-raise. self.destroy() raise - def cbok(x): - print 'vm_create> cbok', x - return x - deferred.addCallback(cbok) print 'vm_create<' return deferred @@ -445,7 +457,7 @@ class XendDomainInfo: """ devices = [] for d in sxp.children(self.config, 'device'): - dev = sxp.child0(d) + dev = sxp.child(d) if dev is None: continue if name == sxp.name(dev): devices.append(dev) @@ -576,7 +588,7 @@ class XendDomainInfo: if self.recreate: return memory = self.memory name = self.name - cpu = int(sxp.child_value(self.config, 'cpu', '-1')) + cpu = int(sxp.child_value(self.config, 'cpu') or '-1') print 'init_domain>', memory, name, cpu dom = xc.domain_create(mem_kb= memory * 1024, name= name, cpu= cpu) if dom <= 0: @@ -641,7 +653,7 @@ class XendDomainInfo: devices = sxp.children(self.config, 'device') index = {} for d in devices: - dev = sxp.child0(d) + dev = sxp.child(d) if dev is None: raise VmError('invalid device') dev_name = sxp.name(dev) @@ -660,7 +672,7 @@ class XendDomainInfo: """Set configuration flags if the vm is a backend for netif of blkif. """ for c in sxp.children(self.config, 'backend'): - name = sxp.name(sxp.child0(c)) + name = sxp.name(sxp.child(c)) if name == 'blkif': self.blkif_backend = 1 elif name == 'netif': @@ -706,6 +718,10 @@ class XendDomainInfo: self.name = d['name'] self.memory = d['memory']/1024 deferred = self.configure() + def cberr(err): + self.destroy() + return err + deferred.addErrback(cberr) except StandardError, ex: self.destroy() raise @@ -738,7 +754,7 @@ def vm_image_linux(vm, image): """ kernel = sxp.child_value(image, "kernel") cmdline = "" - ip = sxp.child_value(image, "ip", "dhcp") + ip = sxp.child_value(image, "ip") or "dhcp" if ip: cmdline += " ip=" + ip root = sxp.child_value(image, "root") @@ -747,7 +763,7 @@ def vm_image_linux(vm, image): args = sxp.child_value(image, "args") if args: cmdline += " " + args - ramdisk = sxp.child_value(image, "ramdisk", '') + ramdisk = sxp.child_value(image, "ramdisk") or '' vifs = vm.config_devices("vif") vm.create_domain("linux", kernel, ramdisk, cmdline, len(vifs)) return vm @@ -764,7 +780,7 @@ def vm_image_netbsd(vm, image): #todo: Same as for linux. Is that right? If so can unify them. kernel = sxp.child_value(image, "kernel") cmdline = "" - ip = sxp.child_value(image, "ip", "dhcp") + ip = sxp.child_value(image, "ip") or "dhcp" if ip: cmdline += "ip=" + ip root = sxp.child_value(image, "root") @@ -793,7 +809,7 @@ def vm_dev_vif(vm, val, index): defer = make_vif(vm.dom, vif, vmac, vm.recreate) def fn(id): dev = xend.netif_dev(vm.dom, vif) - devid = sxp.attribute(val, 'id') + devid = sxp.child_value(val, 'id') if devid: dev.setprop('id', devid) bridge = sxp.child_value(val, "bridge") @@ -820,7 +836,7 @@ def vm_dev_vbd(vm, val, index): dev = sxp.child_value(val, 'dev') if not dev: raise VmError('vbd: Missing dev') - mode = sxp.child_value(val, 'mode', 'r') + mode = sxp.child_value(val, 'mode') or 'r' defer = make_disk(vm.dom, uname, dev, mode, vm.recreate) def fn(vbd): dev = xend.blkif_dev(vm.dom, vdev) @@ -922,7 +938,7 @@ def vm_field_vnet(vm, config, val, index): if id is None: raise VmError('vnet: missing vif id') dev = vm.get_device_by_id('vif', id) - #vnet = sxp.child_value(v, 'vnet', 1) + #vnet = sxp.child_value(v, 'vnet') or '1' #mac = sxp.child_value(dev, 'mac') #vif = sxp.child_value(dev, 'vif') #vnet_bridge(vnet, mac, vm.dom, 0) diff --git a/tools/python/xen/xend/XendMigrate.py b/tools/python/xen/xend/XendMigrate.py index 1580ba83ed..a598913792 100644 --- a/tools/python/xen/xend/XendMigrate.py +++ b/tools/python/xen/xend/XendMigrate.py @@ -2,22 +2,125 @@ import sys import socket +import time + +from twisted.internet import reactor +from twisted.internet import defer +defer.Deferred.debug = 1 +from twisted.internet.protocol import Protocol +from twisted.internet.protocol import ClientFactory import sxp import XendDB import EventServer; eserver = EventServer.instance() +from xen.xend.packing import SxpPacker, SxpUnpacker +from xen.xend import XendDomain +xd = XendDomain.instance() + + +XFRD_PORT = 8002 + +XFR_PROTO_MAJOR = 1 +XFR_PROTO_MINOR = 0 + +class Migrate(Protocol): + + def __init__(self, minfo): + self.packer = None + self.unpacker = None + self.minfo = minfo + + def connectionMade(self): + self.packer = SxpPacker(self.transport) + self.unpacker = SxpPacker() + # Send hello. + self.packer.pack(['xfr.hello', XFR_PROTO_MAJOR, XFR_PROTO_MINOR]) + # Send migrate. + vmconfig = self.minfo.vmconfig() + if not vmconfig: + self.loseConnection() + return + self.packer.pack(['xfr.migrate', + self.minfo.src_dom, + vmconfig, + self.minfo.dst_host, + self.minfo.dst_port]) + + def connectionLost(self, reason): + self.minfo.closed(reason) + + def dataReceived(self, data): + try: + self.unpacker.reset(data) + val = self.unpacker.unpack() + print 'dataReceived>', 'val=', val + op = val[0] + op.replace('.', '_') + if op.startwith('xfr_'): + fn = getattr(self, op, self.unknown) + else: + fn = self.unknown + fn(val) + except Exception, ex: + print 'dataReceived>', ex + pass + + def unknown(self, val): + print 'unknown>', val + + def xfr_progress(self, val): + print 'xfr_progress>', val + + def xfr_error(self, val): + # If we get an error with non-zero code the migrate failed. + # An error with code zero indicates hello success. + err = int(val[1]) + if not err: return + self.minfo.error(err); + self.loseConnection() + + def xfr_ok(self, val): + # An ok indicates migrate completed successfully, and contains + # the new domain id on the remote system. + dom = int(val[1]) + self.minfo.ok(dom) + self.loseConnection() + +class MigrateClientFactory(ClientFactory): + + def __init__(self, minfo): + ClientFactory.__init__(self) + self.minfo = minfo + + def startedConnecting(self, connector): + print 'Started to connect', 'self=', self, 'connector=', connector + + def buildProtocol(self, addr): + print 'buildProtocol>', addr + return Migrate(self.minfo) + + def clientConnectionLost(self, connector, reason): + print 'clientConnectionLost>', 'connector=', connector, 'reason=', reason + + def clientConnectionFailed(self, connector, reason): + print 'clientConnectionFailed>', 'connector=', connector, 'reason=', reason + + class XendMigrateInfo: # states: begin, active, failed, succeeded? - def __init__(self, id, dom, dst): + def __init__(self, id, dom, host, port): self.id = id self.state = 'begin' self.src_host = socket.gethostname() self.src_dom = dom self.dst_host = dst + self.dst_port = port self.dst_dom = None + self.start = 0 + self.deferred = defer.Deferred() def set_state(self, state): self.state = state @@ -29,12 +132,33 @@ class XendMigrateInfo: sxpr = ['migrate', ['id', self.id], ['state', self.state] ] sxpr_src = ['src', ['host', self.src_host], ['domain', self.src_dom] ] sxpr.append(sxpr_src) - sxpr_dst = ['dst', ['host', self.dst] ] + sxpr_dst = ['dst', ['host', self.dst_host] ] if self.dst_dom: sxpr_dst.append(['domain', self.dst_dom]) sxpr.append(sxpr_dst) return sxpr - + + def vmconfig(self): + dominfo = xd.domain_get(self.dom) + if dominfo: + val = return sxp.to_string(dominfo) + else: + val = None + return None + + def error(self, err): + self.state = 'error' + + def ok(self, dom): + self.state = 'ok' + self.dst_dom = dom + + def close(self): + if self.state =='ok': + eserver.inject('xend.migrate.ok', self.sxpr()) + else: + self.state = 'error' + eserver.inject('xend.migrate.error', self.sxpr()) class XendMigrate: # Represents migration in progress. @@ -84,15 +208,15 @@ class XendMigrate: def migrate_get(self, id): return self.migrate.get(id) - def migrate_begin(self, dom, dst): + def migrate_begin(self, dom, host): # Check dom for existence, not migrating already. - # Create migrate info, tell xend to migrate it? - # - or fork migrate command ourselves? # Subscribe to migrate notifications (for updating). id = self.nextid() - info = XenMigrateInfo(id, dom, dst) + info = XenMigrateInfo(id, dom, host, XFRD_PORT) self._add_migrate(id, info) - return id + mcf = MigrateClientFactory(info) + reactor.connectTCP('localhost', XFRD_PORT, mcf) + return info.deferred def instance(): global inst diff --git a/tools/python/xen/xend/XendRoot.py b/tools/python/xen/xend/XendRoot.py index 665f5df29e..3687f57f4a 100644 --- a/tools/python/xen/xend/XendRoot.py +++ b/tools/python/xen/xend/XendRoot.py @@ -145,7 +145,7 @@ class XendRoot: val default value (optional, defaults to None) returns value """ - return sxp.child_value(self.config, name, val=val) + return sxp.child_value(self.config, name) or val def instance(): global inst diff --git a/tools/python/xen/xend/packing.py b/tools/python/xen/xend/packing.py new file mode 100644 index 0000000000..760af7f5a1 --- /dev/null +++ b/tools/python/xen/xend/packing.py @@ -0,0 +1,329 @@ + +# XDR-style packer/unpacker for sxpr. +# +# string -> [STRING] [len:u16] +# atom -> [ATOM] [len:u16] +# int -> [UINT] [value] +# list -> [LIST] {1 elt}* 0 +# null -> [NULL] +# none -> [NONE] +# bool -> [BOOL] { 0:u8 | 1:u8 } +# +# types packed as u16. +# +# So (a b c) -> [LIST] 1 a 1 b 1 c 0 +# () -> [LIST] 0 + +import struct + +try: + from cStringIO import StringIO as _StringIO +except ImportError: + from StringIO import StringIO as _StringIO + +import types + +class Error(Exception): + + def __init__(self, msg): + self.msg = msg + + def __repr__(self): + return repr(self.msg) + + def __str__(self): + return str(self.msg) + + +class ConversionError(Error): + pass + +BOOL_SIZE = 1 +BOOL_FMT = '>B' + +BYTE_SIZE = 1 +BYTE_FMT = '>b' +UBYTE_FMT = '>B' + +SHORT_SIZE = 2 +SHORT_FMT = '>h' +USHORT_FMT = '>H' + +INT_SIZE = 4 +INT_FMT = '>l' +UINT_FMT = '>L' + +NONE_CODE = 0 +NULL_CODE = 1 +INT_CODE = 2 +STRING_CODE = 3 +ATOM_CODE = 4 +BOOL_CODE = 5 +LIST_CODE = 10 + +class Packer: + + def __init__(self, io=None): + self.reset(io=io) + + def reset(self, io=None): + if io is None: + io = _StringIO() + self.io = io + + def get_buffer(self): + return self.io.getvalue() + + def get_io(self): + return self.io + + def struct_pack(self, fmt, x): + try: + self.io.write(struct.pack(fmt, x)) + except struct.error, msg: + raise ConversionError, msg + + def pack_none(self): + pass + + def pack_bool(self, x): + # { '1' | '0' } + print 'bool>', x + if x: + self.io.write('\1') + else: + self.io.write('\0') + + def pack_byte(self, x): + self.struct_pack(BYTE_FMT, x & 0xff) + + def pack_char(self, x): + print 'char>', x + self.io.write(x) + + def pack_ubyte(self, x): + print 'ubyte>', x + self.struct_pack(UBYTE_FMT, x & 0xff) + + def pack_ushort(self, x): + print 'ushort>', x + self.struct_pack(USHORT_FMT, x & 0xffff) + + def pack_short(self, x): + print 'short>', x + self.struct_pack(SHORT_FMT, x & 0xffff) + + def pack_uint(self, x): + print 'uint>', x + self.struct_pack(UINT_FMT, x) + + def pack_int(self, x): + print 'int>', x + self.struct_pack(INT_FMT, x) + + def pack_uhyper(self, x): + print 'uhyper>', x + self.pack_uint(x>>32 & 0xffffffffL) + self.pack_uint(x & 0xffffffffL) + + pack_hyper = pack_uhyper + + def pack_fstring(self, n, x): + print 'fstring>', x + self.io.write(x) + + pack_fopaque = pack_fstring + + def pack_string(self, x): + print 'string>', x + n = len(x) + self.pack_ushort(n) + self.pack_fstring(n, x) + + pack_opaque = pack_string + pack_bytes = pack_string + + def pack_list(self, x, pack_item): + print 'list>', x + # { '1' }* '0' + for item in x: + self.pack_bool(1) + pack_item(item) + self.pack_bool(0) + + def pack_farray(self, x, pack_item): + # * + # Can pass n and check length - but is it worth it? + print 'farray>', list + for item in x: + pack_item(item) + + def pack_array(self, x, pack_item): + # n *n + print 'array>', x + self.pack_uint(len(x)) + self.pack_farray(x, pack_item) + +class Unpacker: + + def __init__(self, data): + self.reset(data) + + def reset(self, data): + if isinstance(data, types.StringType): + data = _StringIO(data) + self.io = data + + def get_bytes(self, n): + if n < 0: + raise ConversionError('negative byte count') + data = self.io.read(n) + return data + + def struct_unpack(self, fmt, n): + data = self.get_bytes(n) + try: + return struct.unpack(fmt, data)[0] + except struct.error, msg: + raise ConversionError, msg + + def unpack_none(self): + return None + + def unpack_bool(self): + return self.struct_unpack(BOOL_FMT, BOOL_SIZE) + + def unpack_char(self): + return self.get_bytes(1)[0] + + def unpack_byte(self): + return self.struct_unpack(BYTE_FMT, BYTE_SIZE) + + def unpack_ubyte(self): + return self.struct_unpack(UBYTE_FMT, BYTE_SIZE) + + def unpack_ushort(self): + return self.struct_unpack(USHORT_FMT, SHORT_SIZE) + + def unpack_short(self): + return self.struct_unpack(SHORT_FMT, SHORT_SIZE) + + def unpack_uint(self): + x = self.struct_unpack(UINT_FMT, UINT_SIZE) + try: + return int(x) + except OverflowError: + return x + + def unpack_int(self): + return self.struct_unpack(INT_FMT, INT_SIZE) + + def unpack_uhyper(self): + hi = self.unpack_uint() + lo = self.unpack_uint() + return long(hi)<<32 | lo + + def unpack_hyper(self): + x = self.unpack_uhyper() + if x >= 0x8000000000000000L: + x = x - 0x10000000000000000L + return x + + def unpack_fstring(self, n): + return self.get_bytes(n) + + unpack_fopaque = unpack_fstring + + def unpack_string(self): + n = self.unpack_ushort() + return self.unpack_fstring(n) + + unpack_opaque = unpack_string + unpack_bytes = unpack_string + + def unpack_list(self, unpack_item): + list = [] + while self.unpack_bool(): + list.append(unpack_item()) + return list + + def unpack_farray(self, n, unpack_item): + list = [] + for i in range(n): + list.append(unpack_item()) + return list + + def unpack_array(self, unpack_item): + n = self.unpack_ushort() + return self.unpack_farray(n, unpack_item) + +class SxpPacker(Packer): + + pack_code = Packer.pack_ushort + + def pack(self, x): + if isinstance(x, types.NoneType): + self.pack_code(NONE_CODE) + self.pack_none() + elif isinstance(x, types.IntType): + self.pack_code(INT_CODE) + self.pack_int(x) + elif isinstance(x, types.StringType): + self.pack_code(STRING_CODE) + self.pack_string(x) + elif isinstance(x, types.ListType): + self.pack_code(LIST_CODE) + self.pack_list(x, self.pack) + else: + raise Error('invalid type ' + str(type(x))) + +class SxpUnpacker(Unpacker): + + unpack_code = Unpacker.unpack_ushort + + def unpack(self): + code = self.unpack_code() + if code == NONE_CODE: + val = self.unpack_none() + elif code == INT_CODE: + val = self.unpack_int() + elif code == BOOL_CODE: + val = self.unpack_bool() + elif code == STRING_CODE: + val = self.unpack_string() + elif code == ATOM_CODE: + val = self.unpack_string() + elif code == LIST_CODE: + val = self.unpack_list(self.unpack) + else: + raise Error('invalid code ' + str(code)) + return val + +def main(): + d = "['vfarm', ['@', ['name', 'vfarm1']], ['memory', 1024], ['image', 'splinux'], ['args', 'root=/dev/nfs ip=dhcp'], [ 1, -1, 1000000]]" + print"> len=", len(d), "d=", d + obj = ['vfarm', ['@', ['name', 'vfarm1']], + ['memory', 1024], + ['image', 'splinux'], + ['args', 'root=/dev/nfs ip=dhcp'], + [ 1, -1, 1000000] ] + print "> obj=", obj + pack = SxpPacker() + pack.pack(obj) + data = pack.get_buffer() + print "> len=", len(data), "data=", data + unpack = SxpUnpacker(data) + obj_unpack = unpack.unpack() + print "> obj=", obj_unpack + #obj = [100,101,102, 999.00234, { 'a': 1, 'b': 2 } ] + #pack.reset() + #pack.pack_item(obj) + #data = pack.get_buffer() + #print "> obj=", obj + #print "> len=", len(data), "data=", data + #unpack.reset(data) + #obj_unpack = unpack.unpack_item() + #print "> obj=", obj_unpack + +if __name__ == "__main__": + main() diff --git a/tools/python/xen/xend/server/SrvBase.py b/tools/python/xen/xend/server/SrvBase.py index bcff1bc3a0..059220c408 100644 --- a/tools/python/xen/xend/server/SrvBase.py +++ b/tools/python/xen/xend/server/SrvBase.py @@ -8,6 +8,7 @@ import types import StringIO from twisted.internet import defer +defer.Deferred.debug = 1 from twisted.internet import reactor from twisted.web import error from twisted.web import resource diff --git a/tools/python/xen/xend/server/SrvDaemon.py b/tools/python/xen/xend/server/SrvDaemon.py index 63c4dd1bee..a514b89108 100644 --- a/tools/python/xen/xend/server/SrvDaemon.py +++ b/tools/python/xen/xend/server/SrvDaemon.py @@ -23,6 +23,7 @@ from twisted.internet import reactor from twisted.internet import protocol from twisted.internet import abstract from twisted.internet import defer +defer.Deferred.debug = 1 from xen.lowlevel import xu diff --git a/tools/python/xen/xend/server/SrvDomain.py b/tools/python/xen/xend/server/SrvDomain.py index 156198bd70..085dd2642e 100644 --- a/tools/python/xen/xend/server/SrvDomain.py +++ b/tools/python/xen/xend/server/SrvDomain.py @@ -27,7 +27,11 @@ class SrvDomain(SrvDir): return val def op_shutdown(self, op, req): - val = self.xd.domain_shutdown(self.dom.id) + #val = self.xd.domain_shutdown(self.dom.id) + fn = FormFn(self.xd.domain_shutdown, + [['dom', 'int'], + ['reason', 'str']]) + val = fn(req.args, {'dom': self.dom.id}) req.setResponseCode(202) req.setHeader("Location", "%s/.." % req.prePathURL()) return val diff --git a/tools/python/xen/xend/server/SrvDomainDir.py b/tools/python/xen/xend/server/SrvDomainDir.py index af4bc7a15c..eb1521e117 100644 --- a/tools/python/xen/xend/server/SrvDomainDir.py +++ b/tools/python/xen/xend/server/SrvDomainDir.py @@ -1,5 +1,6 @@ # Copyright (C) 2004 Mike Wray +import traceback from StringIO import StringIO from twisted.protocols import http @@ -24,6 +25,7 @@ class SrvDomainDir(SrvDir): val = None try: dom = self.xd.domain_get(x) + if not dom: raise KeyError('No such domain') val = SrvDomain(dom) except KeyError, ex: print 'SrvDomainDir>', ex @@ -38,6 +40,9 @@ class SrvDomainDir(SrvDir): return v def op_create(self, op, req): + """Create a domain. + Expects the domain config in request parameter 'config' in SXP format. + """ ok = 0 try: configstring = req.args.get('config')[0] @@ -48,7 +53,8 @@ class SrvDomainDir(SrvDir): config = pin.get_val() ok = 1 except Exception, ex: - print 'op_create>', ex + print 'op_create> Exception in config', ex + traceback.print_exc() if not ok: req.setResponseCode(http.BAD_REQUEST, "Invalid configuration") return "Invalid configuration" @@ -57,19 +63,20 @@ class SrvDomainDir(SrvDir): "Invalid configuration") try: deferred = self.xd.domain_create(config) - deferred.addCallback(self._cb_op_create, configstring, req) + deferred.addCallback(self._op_create_cb, configstring, req) + deferred.addErrback(self._op_create_err, req) return deferred except Exception, ex: - raise - #return ['err', str(ex) ] - #req.setResponseCode(http.BAD_REQUEST, "Error creating domain") - #return str(ex) + print 'op_create> Exception creating domain:' + traceback.print_exc() + req.setResponseCode(http.BAD_REQUEST, "Error creating domain") + return str(ex) #return error.ErrorPage(http.BAD_REQUEST, # "Error creating domain", # str(ex)) - def _cb_op_create(self, dominfo, configstring, req): + def _op_create_cb(self, dominfo, configstring, req): """Callback to handle deferred domain creation. """ dom = dominfo.id @@ -89,7 +96,16 @@ class SrvDomainDir(SrvDir): out.close() return val + def _op_create_err(self, err, req): + """Callback to handle errors in deferred domain creation. + """ + print 'op_create> Deferred Exception creating domain:', err + req.setResponseCode(http.BAD_REQUEST, "Error creating domain") + return str(err) + def op_restore(self, op, req): + """Restore a domain from file. + """ fn = FormFn(self.xd.domain_restore, [['file', 'str']]) val = fn(req.args) @@ -131,6 +147,8 @@ class SrvDomainDir(SrvDir): req.write('') def form(self, req): + """Generate the form(s) for domain dir operations. + """ req.write('
' % req.prePathURL()) req.write('') diff --git a/tools/python/xen/xend/server/blkif.py b/tools/python/xen/xend/server/blkif.py index 4e2a49f7d8..5596ddb842 100755 --- a/tools/python/xen/xend/server/blkif.py +++ b/tools/python/xen/xend/server/blkif.py @@ -1,4 +1,7 @@ +# Copyright (C) 2004 Mike Wray + from twisted.internet import defer +defer.Deferred.debug = 1 from xen.xend import sxp from xen.xend import PrettyPrint @@ -18,53 +21,78 @@ class BlkifControllerFactory(controller.ControllerFactory): self.majorTypes = [ CMSG_BLKIF_BE ] self.subTypes = { - CMSG_BLKIF_BE_CREATE : self.recv_be_create, - CMSG_BLKIF_BE_CONNECT : self.recv_be_connect, - CMSG_BLKIF_BE_VBD_CREATE : self.recv_be_vbd_create, - CMSG_BLKIF_BE_VBD_GROW : self.recv_be_vbd_grow, CMSG_BLKIF_BE_DRIVER_STATUS_CHANGED: self.recv_be_driver_status_changed, } self.attached = 1 self.registerChannel() def createInstance(self, dom, recreate=0): - d = self.addDeferred() + """Create a block device controller for a domain. + + dom domain + recreate if true it's a recreate (after xend restart) + """ + d = defer.Deferred() blkif = self.getInstanceByDom(dom) if blkif: - self.callDeferred(blkif) + d.callback(blkif) else: blkif = BlkifController(self, dom) self.addInstance(blkif) if recreate: - self.callDeferred(blkif) + d.callback(blkif) else: - blkif.send_be_create() + d1 = defer.Deferred() + d1.addCallback(self.respond_be_create, d) + d1.addErrback(d.errback) + blkif.send_be_create(response=d1) return d def getDomainDevices(self, dom): + """Get the block devices for a domain. + + dom domain + + returns devices + """ blkif = self.getInstanceByDom(dom) return (blkif and blkif.getDevices()) or [] def getDomainDevice(self, dom, vdev): + """Get a block device from a domain. + + dom domain + vdev device index + + returns device + """ blkif = self.getInstanceByDom(dom) return (blkif and blkif.getDevice(vdev)) or None def setControlDomain(self, dom, recreate=0): + """Set the back-end block device controller domain. + + dom domain + recreate if true it's a recreate (after xend restart) + """ if self.dom == dom: return self.deregisterChannel() if not recreate: self.attached = 0 self.dom = dom self.registerChannel() - # - #if xend.blkif.be_port: - # xend.blkif.recovery = True - #xend.blkif.be_port = xend.main.port_from_dom(dom) def getControlDomain(self): + """Get the back-end block device controller domain. + """ return self.dom def reattachDevice(self, dom, vdev): + """Reattach a device (on changing control domain). + + dom domain + vdev device index + """ blkif = self.getInstanceByDom(dom) if blkif: blkif.reattachDevice(vdev) @@ -83,17 +111,20 @@ class BlkifControllerFactory(controller.ControllerFactory): return attached def reattached(self): + """Notify all block interface we have been reattached + (after changing control domain). + """ for blkif in self.getInstances(): blkif.reattached() - def recv_be_create(self, msg, req): - #print 'recv_be_create>' + def respond_be_create(self, msg, d): + print 'respond_be_create>' val = unpackMsg('blkif_be_create_t', msg) blkif = self.getInstanceByDom(val['domid']) - self.callDeferred(blkif) + d.callback(blkif) - def recv_be_connect(self, msg, req): - #print 'recv_be_create>' + def respond_be_connect(self, msg): + print 'respond_be_connect>', self val = unpackMsg('blkif_be_connect_t', msg) blkif = self.getInstanceByDom(val['domid']) if blkif: @@ -101,25 +132,30 @@ class BlkifControllerFactory(controller.ControllerFactory): else: pass - def recv_be_vbd_create(self, msg, req): - #print 'recv_be_vbd_create>' + def respond_be_vbd_create(self, msg, d): + print 'recv_be_vbd_create>', self val = unpackMsg('blkif_be_vbd_create_t', msg) blkif = self.getInstanceByDom(val['domid']) if blkif: - blkif.send_be_vbd_grow(val['vdevice']) + d1 = defer.Deferred() + d1.addCallback(self.respond_be_vbd_grow, d) + if d: d1.addErrback(d.errback) + blkif.send_be_vbd_grow(val['vdevice'], response=d1) else: pass - def recv_be_vbd_grow(self, msg, req): - #print 'recv_be_vbd_grow>' + def respond_be_vbd_grow(self, msg, d): + print 'recv_be_vbd_grow>', self val = unpackMsg('blkif_be_vbd_grow_t', msg) # Check status? if self.attached: - self.callDeferred(0) + if d: + d.callback(0) else: self.reattachDevice(val['domid'], val['vdevice']) def recv_be_driver_status_changed(self, msg, req): + print 'recv_be_driver_status_changed>', self, req val = unpackMsg('blkif_be_driver_status_changed_t', msg) status = val['status'] if status == BLKIF_DRIVER_STATUS_UP and not self.attached: @@ -147,7 +183,6 @@ class BlkDev(controller.Dev): return val def destroy(self): - print 'BlkDev>destroy>', self.vdev PrettyPrint.prettyprint(self.sxpr()) self.controller.send_be_vbd_destroy(self.vdev) @@ -157,7 +192,6 @@ class BlkifController(controller.Controller): """ def __init__(self, factory, dom): - #print 'BlkifController> dom=', dom controller.Controller.__init__(self, factory, dom) self.devices = {} @@ -172,7 +206,6 @@ class BlkifController(controller.Controller): self.attached = 1 self.evtchn = None self.registerChannel() - #print 'BlkifController<', 'dom=', self.dom, 'idx=', self.idx def sxpr(self): val = ['blkif', ['dom', self.dom]] @@ -182,11 +215,6 @@ class BlkifController(controller.Controller): self.evtchn['port2']]) return val - def lostChannel(self): - print 'BlkifController>lostChannel>', 'dom=', self.dom - #self.destroyDevices() - controller.Controller.lostChannel(self) - def getDevices(self): return self.devices.values() @@ -201,26 +229,32 @@ class BlkifController(controller.Controller): def attachDevice(self, vdev, mode, segment, recreate=0): """Attach a device to the specified interface. + + vdev device index + mode read/write mode + segment segment + recreate if true it's being recreated (after xend restart) + + returns deferred """ - #print 'BlkifController>attach_device>', self.dom, vdev, mode, segment dev = self.addDevice(vdev, mode, segment) if not dev: return -1 + d = defer.Deferred() if recreate: - d = defer.Deferred() d.callback(self) else: - self.send_be_vbd_create(vdev) - d = self.factory.addDeferred() + d1 = defer.Deferred() + d1.addCallback(self.factory.respond_be_vbd_create, d) + d1.addErrback(d.errback) + self.send_be_vbd_create(vdev, response=d1) return d def destroy(self): - print 'BlkifController>destroy> dom=', self.dom def cb_destroy(val): self.send_be_destroy() - d = self.factory.addDeferred() + d = defer.Deferred() d.addCallback(cb_destroy) - self.send_be_disconnect() - #self.destroyDevices() + self.send_be_disconnect(response=d) def destroyDevices(self): for dev in self.getDevices(): @@ -232,7 +266,9 @@ class BlkifController(controller.Controller): self.attached = 0 for dev in self.devices.values(): dev.attached = 0 - self.send_be_vbd_create(vdev) + d1 = defer.Deferred() + d1.addCallback(self.factory.respond_be_vbd_create, None) + self.send_be_vbd_create(vdev, response=d1) def reattachDevice(self, vdev): """Reattach a device, when the back-end control domain has changed. @@ -273,51 +309,47 @@ class BlkifController(controller.Controller): 'blkif_handle' : val['handle'], 'evtchn' : self.evtchn['port1'], 'shmem_frame' : val['shmem_frame'] }) - self.factory.writeRequest(msg) - pass + d = defer.Deferred() + d.addCallback(self.factory.respond_be_connect) + self.factory.writeRequest(msg, response=d) - #def recv_fe_interface_status_changed(self, msg, req): - # (hnd, status, chan) = unpackMsg('blkif_fe_interface_status_changed_t', msg) - # print 'recv_fe_interface_status_changed>', hnd, status, chan - # pass - - def send_fe_interface_status_changed(self): + def send_fe_interface_status_changed(self, response=None): msg = packMsg('blkif_fe_interface_status_changed_t', { 'handle' : 0, 'status' : BLKIF_INTERFACE_STATUS_CONNECTED, 'evtchn' : self.evtchn['port2'] }) - self.writeRequest(msg) + self.writeRequest(msg, response=response) - def send_be_create(self): + def send_be_create(self, response=None): msg = packMsg('blkif_be_create_t', { 'domid' : self.dom, 'blkif_handle' : 0 }) - self.factory.writeRequest(msg) + self.factory.writeRequest(msg, response=response) - def send_be_disconnect(self): + def send_be_disconnect(self, response=None): print '>BlkifController>send_be_disconnect>', 'dom=', self.dom msg = packMsg('blkif_be_disconnect_t', { 'domid' : self.dom, 'blkif_handle' : 0 }) - self.factory.writeRequest(msg) + self.factory.writeRequest(msg, response=response) - def send_be_destroy(self): + def send_be_destroy(self, response=None): print '>BlkifController>send_be_destroy>', 'dom=', self.dom msg = packMsg('blkif_be_destroy_t', { 'domid' : self.dom, 'blkif_handle' : 0 }) - self.factory.writeRequest(msg) + self.factory.writeRequest(msg, response=response) - def send_be_vbd_create(self, vdev): + def send_be_vbd_create(self, vdev, response=None): dev = self.devices[vdev] msg = packMsg('blkif_be_vbd_create_t', { 'domid' : self.dom, 'blkif_handle' : 0, 'vdevice' : dev.vdev, 'readonly' : dev.readonly() }) - self.factory.writeRequest(msg) + self.factory.writeRequest(msg, response=response) - def send_be_vbd_grow(self, vdev): + def send_be_vbd_grow(self, vdev, response=None): dev = self.devices[vdev] msg = packMsg('blkif_be_vbd_grow_t', { 'domid' : self.dom, @@ -326,9 +358,9 @@ class BlkifController(controller.Controller): 'extent.device' : dev.device, 'extent.sector_start' : dev.start_sector, 'extent.sector_length' : dev.nr_sectors }) - self.factory.writeRequest(msg) + self.factory.writeRequest(msg, response=response) - def send_be_vbd_destroy(self, vdev): + def send_be_vbd_destroy(self, vdev, response=None): print '>BlkifController>send_be_vbd_destroy>', 'dom=', self.dom, 'vdev=', vdev PrettyPrint.prettyprint(self.sxpr()) dev = self.devices[vdev] @@ -337,5 +369,5 @@ class BlkifController(controller.Controller): 'blkif_handle' : 0, 'vdevice' : dev.vdev }) del self.devices[vdev] - self.factory.writeRequest(msg) + self.factory.writeRequest(msg, response=response) diff --git a/tools/python/xen/xend/server/channel.py b/tools/python/xen/xend/server/channel.py index da19d621c2..caaa66e28c 100755 --- a/tools/python/xen/xend/server/channel.py +++ b/tools/python/xen/xend/server/channel.py @@ -1,3 +1,5 @@ +# Copyright (C) 2004 Mike Wray + import xen.lowlevel.xc; xc = xen.lowlevel.xc.new() from xen.lowlevel import xu from messages import msgTypeName @@ -24,14 +26,11 @@ class ChannelFactory: self.notifier = xu.notifier() def addChannel(self, channel): - """Add a channel. + """Add a channel. Registers with the notifier. """ idx = channel.idx self.channels[idx] = channel self.notifier.bind(idx) - # Try to wake it up - #self.notifier.unmask(idx) - #channel.notify() def getChannel(self, idx): """Get the channel with the given index (if any). @@ -40,6 +39,7 @@ class ChannelFactory: def delChannel(self, idx): """Remove the channel with the given index (if any). + Deregisters with the notifier. """ if idx in self.channels: del self.channels[idx] @@ -174,9 +174,9 @@ class VirqChannel(BaseChannel): """Close the channel. Calls lostChannel(self) on all its clients and channelClosed() on the factory. """ - for c in self.clients: + for c in self.clients[:]: c.lostChannel(self) - del self.clients + self.clients = [] BaseChannel.close(self) def registerClient(self, client): @@ -238,7 +238,7 @@ class Channel(BaseChannel): """ if self.closed: return self.closed = 1 - for d in self.devs: + for d in self.devs[:]: d.lostChannel() self.factory.channelClosed(self) self.devs = [] @@ -288,6 +288,8 @@ class Channel(BaseChannel): self.getRemotePort())) def handleNotification(self): + """Process outstanding messages in repsonse to notification on the port. + """ if self.closed: print 'handleNotification> Notification on closed channel', self return @@ -299,6 +301,8 @@ class Channel(BaseChannel): self.notify() def notify(self): + """Notify the other end of the port that messages have been processed. + """ if self.closed: return self.port.notify() diff --git a/tools/python/xen/xend/server/console.py b/tools/python/xen/xend/server/console.py index 3227dc1b5e..77f6343c3d 100755 --- a/tools/python/xen/xend/server/console.py +++ b/tools/python/xen/xend/server/console.py @@ -1,3 +1,4 @@ +# Copyright (C) 2004 Mike Wray from twisted.internet import reactor from twisted.internet import protocol @@ -12,11 +13,6 @@ import controller from messages import * from params import * -"""Telnet binary option.""" -TRANSMIT_BINARY = '0' -WILL = chr(251) -IAC = chr(255) - class ConsoleProtocol(protocol.Protocol): """Asynchronous handler for a console TCP socket. """ @@ -36,19 +32,12 @@ class ConsoleProtocol(protocol.Protocol): self.loseConnection() return else: - # KAF: A nice quiet successful connect. Don't bother with telnet - # control sequence -- telnet is not the appropriate protocol here. + # KAF: A nice quiet successful connect. #self.transport.write("Connected to console %d on domain %d\n" # % (self.idx, self.controller.dom)) - #self.setTelnetTransmitBinary() eserver.inject('xend.console.connect', [self.idx, self.addr[0], self.addr[1]]) - def setTelnetTransmitBinary(self): - """Send the sequence to set the telnet TRANSMIT-BINARY option. - """ - self.write(IAC + WILL + TRANSMIT_BINARY) - def dataReceived(self, data): if self.controller.handleInput(self, data): self.loseConnection() @@ -104,11 +93,15 @@ class ConsoleController(controller.Controller): output and the connected TCP sockets to post console input. """ + STATUS_NEW = 'new' + STATUS_CLOSED = 'closed' + STATUS_CONNECTED = 'connected' + STATUS_LISTENING = 'listening' + def __init__(self, factory, dom, console_port): - #print 'ConsoleController> dom=', dom, type(dom) controller.Controller.__init__(self, factory, dom) self.majorTypes = [ CMSG_CONSOLE ] - self.status = "new" + self.status = self.STATUS_NEW self.addr = None self.conn = None self.rbuf = xu.buffer() @@ -118,7 +111,6 @@ class ConsoleController(controller.Controller): self.registerChannel() self.listener = None self.listen() - #print 'ConsoleController<', 'dom=', self.dom, 'idx=', self.idx def sxpr(self): val =['console', @@ -136,29 +128,31 @@ class ConsoleController(controller.Controller): return not (self.closed() or self.rbuf.empty()) def closed(self): - return self.status == 'closed' + return self.status == self.STATUS_CLOSED def connected(self): - return self.status == 'connected' + return self.status == self.STATUS_CONNECTED def close(self): - try: - #print 'ConsoleController> close dom=', self.dom - self.status = "closed" - if self.conn: - self.conn.loseConnection() - self.listener.stopListening() - self.deregisterChannel() - self.lostChannel() - except Exception, ex: - print 'ConsoleController>close>', ex - raise + """Close the console controller. + """ + self.lostChannel() + + def lostChannel(self): + """The channel to the domain has been lost. + Cleanup: disconnect TCP connections and listeners, notify the controller. + """ + self.status = self.STATUS_CLOSED + if self.conn: + self.conn.loseConnection() + self.listener.stopListening() + controller.Controller.lostChannel(self) def listen(self): """Listen for TCP connections to the console port.. """ if self.closed(): return - self.status = "listening" + self.status = self.STATUS_LISTENING if self.listener: #self.listener.startListening() pass @@ -167,15 +161,25 @@ class ConsoleController(controller.Controller): self.listener = reactor.listenTCP(self.console_port, f) def connect(self, addr, conn): + """Connect a TCP connection to the console. + Fails if closed or already connected. + + addr peer address + conn connection + + returns 0 if ok, negative otherwise + """ if self.closed(): return -1 if self.connected(): return -1 self.addr = addr self.conn = conn - self.status = "connected" + self.status = self.STATUS_CONNECTED self.handleOutput() return 0 def disconnect(self): + """Disconnect the TCP connection to the console. + """ if self.conn: self.conn.loseConnection() self.addr = None @@ -183,15 +187,29 @@ class ConsoleController(controller.Controller): self.listen() def requestReceived(self, msg, type, subtype): - #print '***Console', self.dom, msg.get_payload() + """Receive console data from the console channel. + + msg console message + type major message type + subtype minor message typ + """ self.rbuf.write(msg.get_payload()) self.handleOutput() def responseReceived(self, msg, type, subtype): + """Handle a response to a request written to the console channel. + Just ignore it because the return values are not interesting. + + msg console message + type major message type + subtype minor message typ + """ pass def produceRequests(self): - # Send as much pending console data as there is room for. + """Write pending console data to the console channel. + Writes as much to the channel as it can. + """ work = 0 while not self.wbuf.empty() and self.channel.writeReady(): msg = xu.message(CMSG_CONSOLE, 0, 0) @@ -201,7 +219,12 @@ class ConsoleController(controller.Controller): def handleInput(self, conn, data): """Handle some external input aimed at the console. - Called from a TCP connection (conn). + Called from a TCP connection (conn). Ignores the input + if the calling connection (conn) is not the one connected + to the console (self.conn). + + conn connection + data input data """ if self.closed(): return -1 if conn != self.conn: return 0 @@ -215,18 +238,14 @@ class ConsoleController(controller.Controller): Sends it to the connected console (if any). """ if self.closed(): - #print 'Console>handleOutput> closed' return -1 if not self.conn: - #print 'Console>handleOutput> not connected' return 0 while not self.rbuf.empty(): try: - #print 'Console>handleOutput> writing...' bytes = self.conn.write(self.rbuf.peek()) if bytes > 0: self.rbuf.discard(bytes) except socket.error, error: pass - #print 'Console>handleOutput<' return 0 diff --git a/tools/python/xen/xend/server/controller.py b/tools/python/xen/xend/server/controller.py index 900c2d55b0..6d0bd9295f 100755 --- a/tools/python/xen/xend/server/controller.py +++ b/tools/python/xen/xend/server/controller.py @@ -1,10 +1,50 @@ +# Copyright (C) 2004 Mike Wray + from twisted.internet import defer +defer.Deferred.debug = 1 import channel from messages import msgTypeName +DEBUG=0 + +class OutOfOrderError(RuntimeError): + """Error reported when a response arrives out of order. + """ + pass + +class Responder: + """Handler for a response to a message. + """ + + def __init__(self, mid, deferred): + """Create a responder. + + mid message id of response to handle + deferred deferred object holding the callbacks + """ + self.mid = mid + self.deferred = deferred + + def responseReceived(self, msg): + if self.deferred.called: return + self.deferred.callback(msg) + + def error(self, err): + if self.deferred.called: return + self.deferred.errback(err) + class CtrlMsgRcvr: """Abstract class for things that deal with a control interface to a domain. + + Instance variables: + + dom : the domain we are a control interface for + majorTypes: list of major message types we are interested in + subTypes : mapping of message subtypes to methods + + channel : channel to the domain + idx : channel index """ @@ -15,50 +55,140 @@ class CtrlMsgRcvr: self.dom = None self.channel = None self.idx = None + self.responders = [] + # Timeout (in seconds) for deferreds. + self.timeout = 10 + + def setTimeout(self, timeout): + self.timeout = timeout def requestReceived(self, msg, type, subtype): + """Dispatch a request to handlers. + + msg message + type major message type + subtype minor message type + """ + msgid = msg.get_header()['id'] + if DEBUG: + print 'requestReceived>', self, msgid, msgTypeName(type, subtype) method = self.subTypes.get(subtype) if method: method(msg, 1) - else: + elif DEBUG: print ('requestReceived> No handler: Message type %s %d:%d' % (msgTypeName(type, subtype), type, subtype)), self def responseReceived(self, msg, type, subtype): + """Dispatch a response to handlers. + + msg message + type major message type + subtype minor message type + """ + msgid = msg.get_header()['id'] + if DEBUG: + print 'responseReceived>', self, msgid, msgTypeName(type, subtype) + if self.callResponders(msg): + return method = self.subTypes.get(subtype) if method: method(msg, 0) - else: + elif DEBUG: print ('responseReceived> No handler: Message type %s %d:%d' % (msgTypeName(type, subtype), type, subtype)), self + def addResponder(self, mid, deferred): + """Add a responder for a message id. + The deferred is called with callback(msg) when a response + with the given message id arrives. Responses are expected + to arrive in order of message id. When a response arrives, + waiting responders for messages with lower id have errback + called with an OutOfOrder error. + + mid message id of response expected + deferred a Deferred to handle the response + + returns Responder + """ + if self.timeout > 0: + deferred.setTimeout(self.timeout) + resp = Responder(mid, deferred) + self.responders.append(resp) + return resp + + def callResponders(self, msg): + """Call any waiting responders for a response message. + + msg response message + + returns 1 if there was a responder for the message, 0 otherwise + """ + hdr = msg.get_header() + mid = hdr['id'] + handled = 0 + while self.responders: + resp = self.responders[0] + if resp.mid > mid: + break + self.responders.pop() + if resp.mid < mid: + print 'handleResponse> Out of order:', resp.mid, mid + resp.error(OutOfOrderError()) + else: + handled = 1 + resp.responseReceived(msg) + break + return handled + def lostChannel(self): + """Called when the channel to the domain is lost. + """ pass def registerChannel(self): - #print 'CtrlMsgRcvr>registerChannel>', self + """Register interest in our major message types with the + channel to our domain. + """ self.channel = self.channelFactory.domChannel(self.dom) self.idx = self.channel.getIndex() if self.majorTypes: self.channel.registerDevice(self.majorTypes, self) def deregisterChannel(self): - #print 'CtrlMsgRcvr>deregisterChannel>', self + """Deregister interest in our major message types with the + channel to our domain. + """ if self.channel: self.channel.deregisterDevice(self) del self.channel def produceRequests(self): + """Produce any queued requests. + + return number produced + """ return 0 - def writeRequest(self, msg): + def writeRequest(self, msg, response=None): + """Write a request to the channel. + + msg message + response Deferred to handle the response (optional) + """ if self.channel: + if DEBUG: print 'CtrlMsgRcvr>writeRequest>', self, msg + if response: + self.addResponder(msg.get_header()['id'], response) self.channel.writeRequest(msg) else: print 'CtrlMsgRcvr>writeRequest>', 'no channel!', self def writeResponse(self, msg): + """Write a response to the channel. + """ if self.channel: + if DEBUG: print 'CtrlMsgRcvr>writeResponse>', self, msg self.channel.writeResponse(msg) else: print 'CtrlMsgRcvr>writeResponse>', 'no channel!', self @@ -66,6 +196,12 @@ class CtrlMsgRcvr: class ControllerFactory(CtrlMsgRcvr): """Abstract class for factories creating controllers. Maintains a table of instances. + + Instance variables: + + instances : mapping of index to controller instance + dlist : list of deferreds + dom : domain """ def __init__(self): @@ -73,55 +209,46 @@ class ControllerFactory(CtrlMsgRcvr): self.instances = {} self.dlist = [] self.dom = 0 - # Timeout (in seconds) for deferreds. - self.timeout = 10 def addInstance(self, instance): + """Add a controller instance (under its index). + """ self.instances[instance.idx] = instance def getInstance(self, idx): + """Get a controller instance from its index. + """ return self.instances.get(idx) def getInstances(self): + """Get a list of all controller instances. + """ return self.instances.values() def getInstanceByDom(self, dom): + """Get the controller instance for the given domain. + """ for inst in self.instances.values(): if inst.dom == dom: return inst return None def delInstance(self, instance): - #print 'ControllerFactory>delInstance>', instance.idx + """Delete an instance from the table. + """ if instance.idx in self.instances: - #print 'ControllerFactory>delInstance> remove', instance.idx del self.instances[instance.idx] def createInstance(self, dom, recreate=0): + """Create an instance. Define in a subclass. + """ raise NotImplementedError() def instanceClosed(self, instance): - #print 'ControllerFactory>instanceClosed>', instance.idx, instance + """Callback called when an instance is closed (usually by the instance). + """ self.delInstance(instance) - def addDeferred(self): - d = defer.Deferred() - if self.timeout > 0: - # The deferred will error if not called before timeout. - d.setTimeout(self.timeout) - self.dlist.append(d) - return d - - def callDeferred(self, *args): - if self.dlist: - d = self.dlist.pop(0) - d.callback(*args) - - def errDeferred(self, *args): - if self.dlist: - d = self.dlist.pop(0) - d.errback(*args) - class Controller(CtrlMsgRcvr): """Abstract class for a device controller attached to a domain. """ @@ -134,15 +261,20 @@ class Controller(CtrlMsgRcvr): self.idx = None def close(self): - self.deregisterChannel() + """Close the controller. + """ self.lostChannel() def lostChannel(self): - #print 'Controller>lostChannel>', self, self.factory + """The controller channel has been lost. + """ + self.deregisterChannel() self.factory.instanceClosed(self) class Dev: - + """Abstract class for a device attached to a device controller. + """ + def __init__(self, controller): self.controller = controller self.props = {} @@ -160,9 +292,6 @@ class Dev: if k in self.props: del self.props[k] - #def __repr__(self): - # return str(self.sxpr()) - def sxpr(self): raise NotImplementedError() diff --git a/tools/python/xen/xend/server/cstruct.py b/tools/python/xen/xend/server/cstruct.py deleted file mode 100755 index 880931b41f..0000000000 --- a/tools/python/xen/xend/server/cstruct.py +++ /dev/null @@ -1,269 +0,0 @@ -import struct - -class Struct: - - maxDepth = 10 - - base = ['x', 'B', 'H', 'I', 'L', 'Q', 'c', 'h', 'i', 'l', 'q', ] - - sizes = {'B': 1, - 'H': 2, - 'I': 4, - 'L': 4, - 'Q': 8, - 'c': 1, - 'h': 2, - 'i': 4, - 'l': 4, - 'q': 8, - 'x': 1, - } - - formats = { - 'int8' : 'B', - 'int16' : 'H', - 'int32' : 'I', - 'int64' : 'Q', - 'u8' : 'B', - 'u16' : 'H', - 'u32' : 'I', - 'u64' : 'Q' - } - - def typedef(self, name, val): - self.formats[name] = val - - def struct(self, name, *f): - self.typedef(name, StructInfo(self, f)) - - def getType(self, name): - return self.formats[name] - - def format(self, ty): - d = 0 - f = ty - while d < self.maxDepth: - d += 1 - f = self.formats[f] - if isinstance(f, StructInfo): - return f.format() - if f in self.base: - return f - return -1 - - def alignedformat(self, ty): - fmt = self.format(ty) - #print 'alignedformat> %s |%s|' %(ty, fmt) - afmt = self.align(fmt) - #print 'alignedformat< %s |%s| |%s|' % (ty, fmt, afmt) - return afmt - - def align(self, fmt): - n1 = 0 - afmt = '' - for a in fmt: - n2 = self.getSize(a) - m = n1 % n2 - if m: - d = (n2 - m) - afmt += 'x' * d - n1 += d - afmt += a - n1 += n2 - return afmt - - def fmtsize(self, fmt): - s = 0 - for f in fmt: - s += self.getSize(f) - return s - - def getSize(self, f): - return self.sizes[f] - - def pack(self, ty, data): - return self.getType(ty).pack(data) - - def unpack(self, ty, data): - return self.getType(ty).unpack(data) - - def show(self): - l = self.formats.keys() - l.sort() - for v in l: - print "%-35s %-10s %s" % (v, self.format(v), self.alignedformat(v)) - - -class StructInfo: - - def __init__(self, s, f): - self.fmt = None - self.structs = s - self.fields = f - - def alignedformat(self): - if self.afmt: return self.afmt - self.afmt = self.structs.align(self.format()) - return self.afmt - - def format(self): - if self.fmt: return self.fmt - fmt = "" - for (ty, name) in self.fields: - fmt += self.formatString(ty) - self.fmt = fmt - return fmt - - def formatString(self, ty): - if ty in self.fields: - ty = self.fields[ty] - return self.structs.format(ty) - - def pack(self, *args): - return struct.pack(self.alignedformat(), *args) - - def unpack(self, data): - return struct.unpack(self.alignedformat(), data) - -types = Struct() - -types.typedef('short' , 'h') -types.typedef('int' , 'i') -types.typedef('long' , 'l') -types.typedef('unsigned short', 'H') -types.typedef('unsigned int' , 'I') -types.typedef('unsigned long' , 'L') -types.typedef('domid_t' , 'u64') -types.typedef('blkif_vdev_t' , 'u16') -types.typedef('blkif_pdev_t' , 'u16') -types.typedef('blkif_sector_t', 'u64') - -types.struct('u8[6]', - ('u8', 'a1'), - ('u8', 'a2'), - ('u8', 'a3'), - ('u8', 'a4'), - ('u8', 'a5'), - ('u8', 'a6')) - -types.struct('blkif_fe_interface_status_changed_t', - ('unsigned int', 'handle'), - ('unsigned int', 'status'), - ('unsigned int', 'evtchn')) - -types.struct('blkif_fe_driver_status_changed_t', - ('unsigned int', 'status'), - ('unsigned int', 'nr_interfaces')) - -types.struct('blkif_fe_interface_connect_t', - ('unsigned int' , 'handle'), - ('unsigned long', 'shmem_frame')) - -types.struct('blkif_fe_interface_disconnect_t', - ('unsigned int', 'handle')) - -types.struct('blkif_extent_t', - ('blkif_pdev_t' , 'device'), - ('blkif_sector_t', 'sector_start'), - ('blkif_sector_t', 'sector_length')) - -types.struct('blkif_be_create_t', - ('domid_t' , 'domid'), - ('unsigned int', 'blkif_handle'), - ('unsigned int', 'status')) - -types.struct('blkif_be_destroy_t', - ('domid_t' , 'domid'), - ('unsigned int', 'blkif_handle'), - ('unsigned int', 'status')) - -types.struct('blkif_be_connect_t', - ('domid_t' , 'domid'), - ('unsigned int' , 'blkif_handle'), - ('unsigned int' , 'evtchn'), - ('unsigned long', 'shmem_frame'), - ('unsigned int' , 'status')) - -types.struct('blkif_be_disconnect_t', - ('domid_t' , 'domid'), - ('unsigned int', 'blkif_handle'), - ('unsigned int', 'status')) - -types.struct('blkif_be_vbd_create_t', - ('domid_t' , 'domid'), #Q - ('unsigned int', 'blkif_handle'), #I - ('blkif_vdev_t', 'vdevice'), #H - ('int' , 'readonly'), #i - ('unsigned int', 'status')) #I - -types.struct('blkif_be_vbd_destroy_t', - ('domid_t' , 'domid'), - ('unsigned int', 'blkif_handle'), - ('blkif_vdev_t', 'vdevice'), - ('unsigned int', 'status')) - -types.struct('blkif_be_vbd_grow_t', - ('domid_t' , 'domid'), #Q - ('unsigned int' , 'blkif_handle'), #I - ('blkif_vdev_t' , 'vdevice'), #H - ('blkif_extent_t', 'extent'), #HQQ - ('unsigned int' , 'status')) #I - -types.struct('blkif_be_vbd_shrink_t', - ('domid_t' , 'domid'), - ('unsigned int', 'blkif_handle'), - ('blkif_vdev_t', 'vdevice'), - ('unsigned int', 'status')) - -types.struct('blkif_be_driver_status_changed_t', - ('unsigned int', 'status'), - ('unsigned int', 'nr_interfaces')) - -types.struct('netif_fe_interface_status_changed_t', - ('unsigned int', 'handle'), - ('unsigned int', 'status'), - ('unsigned int', 'evtchn'), - ('u8[6]', 'mac')) - -types.struct('netif_fe_driver_status_changed_t', - ('unsigned int', 'status'), - ('unsigned int', 'nr_interfaces')) - -types.struct('netif_fe_interface_connect_t', - ('unsigned int', 'handle'), - ('unsigned long', 'tx_shmem_frame'), - ('unsigned long', 'rx_shmem_frame')) - -types.struct('netif_fe_interface_disconnect_t', - ('unsigned int', 'handle')) - -types.struct('netif_be_create_t', - ('domid_t' , 'domid'), - ('unsigned int', 'netif_handle'), - ('u8[6]' , 'mac'), - ('unsigned int', 'status')) - -types.struct('netif_be_destroy_t', - ('domid_t' , 'domid'), - ('unsigned int', 'netif_handle'), - ('unsigned int', 'status')) - -types.struct('netif_be_connect_t', - ('domid_t' , 'domid'), - ('unsigned int' , 'netif_handle'), - ('unsigned int' , 'evtchn'), - ('unsigned long', 'tx_shmem_frame'), - ('unsigned long', 'rx_shmem_frame'), - ('unsigned int' , 'status')) - -types.struct('netif_be_disconnect_t', - ('domid_t' , 'domid'), - ('unsigned int', 'netif_handle'), - ('unsigned int', 'status')) - -types.struct('netif_be_driver_status_changed_t', - ('unsigned int', 'status'), - ('unsigned int', 'nr_interfaces')) - -if 1 or __name__ == "__main__": - types.show() diff --git a/tools/python/xen/xend/server/domain.py b/tools/python/xen/xend/server/domain.py index ab22234480..683d352ec0 100644 --- a/tools/python/xen/xend/server/domain.py +++ b/tools/python/xen/xend/server/domain.py @@ -1,3 +1,5 @@ +# Copyright (C) 2004 Mike Wray + import channel import controller from messages import * @@ -7,11 +9,23 @@ class DomainControllerFactory(controller.ControllerFactory): """ def createInstance(self, dom): + """Create a domain controller. + + dom domain + + returns domain controller + """ d = DomainController(self, dom) self.addInstance(d) return d def getInstanceByDom(self, dom): + """Get a domain controller for a domain, creating if necessary. + + dom domain + + returns domain controller + """ for inst in self.instances.values(): if inst.dom == dom: return inst @@ -21,8 +35,11 @@ class DomainControllerFactory(controller.ControllerFactory): class DomainController(controller.Controller): """Generic controller for a domain. + Used for domain shutdown. """ + """Map shutdown reasons to the message type to use. + """ reasons = {'poweroff' : 'shutdown_poweroff_t', 'reboot' : 'shutdown_reboot_t', 'suspend' : 'shutdown_suspend_t' } @@ -34,6 +51,10 @@ class DomainController(controller.Controller): print 'DomainController>', self, self.channel, self.idx def shutdown(self, reason): + """Shutdown a domain. + + reason shutdown reason + """ msgtype = self.reasons.get(reason) if not msgtype: raise ValueError('invalid reason:' + reason) diff --git a/tools/python/xen/xend/server/messages.py b/tools/python/xen/xend/server/messages.py index f3257ca832..edda5f2303 100644 --- a/tools/python/xen/xend/server/messages.py +++ b/tools/python/xen/xend/server/messages.py @@ -148,6 +148,9 @@ netif_formats = { msg_formats.update(netif_formats) #============================================================================ +# Domain shutdown message types. +#============================================================================ + CMSG_SHUTDOWN = 6 CMSG_SHUTDOWN_POWEROFF = 0 @@ -176,8 +179,23 @@ msg_formats.update(shutdown_formats) class Msg: pass +_next_msgid = 0 + +def nextid(): + global _next_msgid + return ++_next_msgid + def packMsg(ty, params): - if DEBUG: print '>packMsg', ty, params + """Pack a message. + Any 'mac' parameter is passed in as an int[6] array and converted. + + ty message type name + params message parameter dict + + returns xu message + """ + msgid = nextid() + if DEBUG: print '>packMsg', msgid, ty, params (major, minor) = msg_formats[ty] args = {} for (k, v) in params.items(): @@ -189,11 +207,19 @@ def packMsg(ty, params): if DEBUG: for (k, v) in args.items(): print 'packMsg>', k, v, type(v) - msgid = 0 msg = xu.message(major, minor, msgid, args) return msg def unpackMsg(ty, msg): + """Unpack a message. + Any mac addresses in the message are converted to int[6] array + in the return dict. + + ty message type + msg xu message + + returns parameter dict + """ args = msg.get_payload() mac = [0, 0, 0, 0, 0, 0] macs = [] @@ -208,10 +234,19 @@ def unpackMsg(ty, msg): args['mac'] = mac for k in macs: del args[k] - if DEBUG: print ' + import random from twisted.internet import defer +defer.Deferred.debug = 1 from xen.xend import sxp from xen.xend import PrettyPrint @@ -21,8 +24,8 @@ class NetifControllerFactory(controller.ControllerFactory): self.majorTypes = [ CMSG_NETIF_BE ] self.subTypes = { - CMSG_NETIF_BE_CREATE : self.recv_be_create, - CMSG_NETIF_BE_CONNECT: self.recv_be_connect, + #CMSG_NETIF_BE_CREATE : self.recv_be_create, + #CMSG_NETIF_BE_CONNECT: self.recv_be_connect, CMSG_NETIF_BE_DRIVER_STATUS_CHANGED: self.recv_be_driver_status_changed, } self.attached = 1 @@ -30,8 +33,12 @@ class NetifControllerFactory(controller.ControllerFactory): def createInstance(self, dom, recreate=0): """Create or find the network interface controller for a domain. + + dom domain + recreate if true this is a recreate (xend restarted) + + returns netif controller """ - #print 'netif>createInstance> dom=', dom netif = self.getInstanceByDom(dom) if netif is None: netif = NetifController(self, dom) @@ -39,15 +46,31 @@ class NetifControllerFactory(controller.ControllerFactory): return netif def getDomainDevices(self, dom): + """Get the network device controllers for a domain. + + dom domain + + returns netif controller + """ netif = self.getInstanceByDom(dom) return (netif and netif.getDevices()) or [] def getDomainDevice(self, dom, vif): + """Get a virtual network interface device for a domain. + + dom domain + vif virtual interface index + + returns NetDev + """ netif = self.getInstanceByDom(dom) return (netif and netif.getDevice(vif)) or None def setControlDomain(self, dom, recreate=0): """Set the 'back-end' device driver domain. + + dom domain + recreate if true this is a recreate (xend restarted) """ if self.dom == dom: return self.deregisterChannel() @@ -55,19 +78,13 @@ class NetifControllerFactory(controller.ControllerFactory): self.attached = 0 self.dom = dom self.registerChannel() - # - #if xend.netif.be_port.remote_dom != 0: - # xend.netif.recovery = True - # xend.netif.be_port = xend.main.port_from_dom(dom) - # def getControlDomain(self): + """Get the domain id of the back-end control domain. + """ return self.dom - def recv_be_create(self, msg, req): - self.callDeferred(0) - - def recv_be_connect(self, msg, req): + def respond_be_connect(self, msg): val = unpackMsg('netif_be_connect_t', msg) dom = val['domid'] vif = val['netif_handle'] @@ -75,7 +92,7 @@ class NetifControllerFactory(controller.ControllerFactory): if netif: netif.send_interface_connected(vif) else: - print "recv_be_connect> unknown vif=", vif + print "respond_be_connect> unknown vif=", vif pass def recv_be_driver_status_changed(self, msg, req): @@ -88,23 +105,6 @@ class NetifControllerFactory(controller.ControllerFactory): netif.reattach_devices() self.attached = 1 -## pl = msg.get_payload() -## status = pl['status'] -## if status == NETIF_DRIVER_STATUS_UP: -## if xend.netif.recovery: -## print "New netif backend now UP, notifying guests:" -## for netif_key in interface.list.keys(): -## netif = interface.list[netif_key] -## netif.create() -## print " Notifying %d" % netif.dom -## msg = xu.message( -## CMSG_NETIF_FE, -## CMSG_NETIF_FE_INTERFACE_STATUS_CHANGED, 0, -## { 'handle' : 0, 'status' : 1 }) -## netif.ctrlif_tx_req(xend.main.port_from_dom(netif.dom),msg) -## print "Done notifying guests" -## recovery = False - class NetDev(controller.Dev): """Info record for a network device. """ @@ -130,9 +130,13 @@ class NetDev(controller.Dev): return val def get_vifname(self): + """Get the virtual interface device name. + """ return "vif%d.%d" % (self.controller.dom, self.vif) def get_mac(self): + """Get the MAC address as a string. + """ return ':'.join(map(lambda x: "%x" % x, self.mac)) def vifctl_params(self): @@ -141,23 +145,31 @@ class NetDev(controller.Dev): 'ipaddr': self.ipaddr } def up(self, bridge=None, ipaddr=[]): + """Bring the device up. + + bridge ethernet bridge to connect to + ipaddr list of ipaddrs to filter using iptables + """ self.bridge = bridge self.ipaddr = ipaddr Vifctl.up(self.get_vifname(), **self.vifctl_params()) def down(self): + """Bring the device down. + """ Vifctl.down(self.get_vifname(), **self.vifctl_params()) def destroy(self): + """Destroy the device's resources and disconnect from the back-end + device controller. + """ def cb_destroy(val): self.controller.send_be_destroy(self.vif) - print 'NetDev>destroy>', 'vif=', self.vif - PrettyPrint.prettyprint(self.sxpr()) self.down() - d = self.controller.factory.addDeferred() + #d = self.controller.factory.addDeferred() + d = defer.Deferred() d.addCallback(cb_destroy) - self.controller.send_be_disconnect(self.vif) - #self.controller.send_be_destroy(self.vif) + self.controller.send_be_disconnect(self.vif, response=d) class NetifController(controller.Controller): @@ -165,7 +177,6 @@ class NetifController(controller.Controller): """ def __init__(self, factory, dom): - #print 'NetifController> dom=', dom controller.Controller.__init__(self, factory, dom) self.devices = {} @@ -178,21 +189,23 @@ class NetifController(controller.Controller): self.recv_fe_interface_connect, } self.registerChannel() - #print 'NetifController<', 'dom=', self.dom, 'idx=', self.idx def sxpr(self): val = ['netif', ['dom', self.dom]] return val def randomMAC(self): - # VIFs get a random MAC address with a "special" vendor id. - # - # NB. The vendor is currently an "obsolete" one that used to belong - # to DEC (AA-00-00). Using it is probably a bit rude :-) - # - # NB2. The first bit of the first random octet is set to zero for - # all dynamic MAC addresses. This may allow us to manually specify - # MAC addresses for some VIFs with no fear of clashes. + """Generate a random MAC address. + + Uses OUI (Organizationally Unique Identifier) AA:00:00, an + unassigned one that used to belong to DEC. The OUI list is + available at 'standards.ieee.org'. + + The remaining 3 fields are random, with the first bit of the first + random field set 0. + + returns array of 6 ints + """ mac = [ 0xaa, 0x00, 0x00, random.randint(0x00, 0x7f), random.randint(0x00, 0xff), @@ -200,29 +213,46 @@ class NetifController(controller.Controller): return mac def lostChannel(self): - print 'NetifController>lostChannel>', 'dom=', self.dom - #self.destroyDevices() + """Method called when the channel has been lost. + """ controller.Controller.lostChannel(self) def getDevices(self): + """Get a list of the devices. + """ return self.devices.values() def getDevice(self, vif): + """Get a device. + + vif device index + + returns device (or None) + """ return self.devices.get(vif) def addDevice(self, vif, vmac): + """Add a network interface. If vmac is None a random MAC is + assigned. If specified, vmac must be a string of the form + XX:XX:XX:XX:XX where X is hex digit. + + vif device index + vmac device MAC + + returns device + """ if vmac is None: mac = self.randomMAC() else: mac = [ int(x, 16) for x in vmac.split(':') ] if len(mac) != 6: raise ValueError("invalid mac") - #print "attach_device>", "vif=", vif, "mac=", mac dev = NetDev(self, vif, mac) self.devices[vif] = dev return dev def destroy(self): - print 'NetifController>destroy>', 'dom=', self.dom + """Destroy the controller and all devices. + """ self.destroyDevices() def destroyDevices(self): @@ -237,20 +267,19 @@ class NetifController(controller.Controller): @param vmac mac address (string) """ self.addDevice(vif, vmac) + d = defer.Deferred() if recreate: - d = defer.Deferred() d.callback(self) else: - d = self.factory.addDeferred() - self.send_be_create(vif) + self.send_be_create(vif, response=d) return d def reattach_devices(self): """Reattach all devices when the back-end control domain has changed. """ - d = self.factory.addDeferred() + #d = self.factory.addDeferred() self.send_be_create(vif) - self.attach_fe_devices(0) + self.attach_fe_devices() def attach_fe_devices(self): for dev in self.devices.values(): @@ -279,38 +308,39 @@ class NetifController(controller.Controller): 'evtchn' : dev.evtchn['port1'], 'tx_shmem_frame' : val['tx_shmem_frame'], 'rx_shmem_frame' : val['rx_shmem_frame'] }) - self.factory.writeRequest(msg) + d = defer.Deferred() + d.addCallback(self.factory.respond_be_connect) + self.factory.writeRequest(msg, response=d) - def send_interface_connected(self, vif): + def send_interface_connected(self, vif, response=None): dev = self.devices[vif] msg = packMsg('netif_fe_interface_status_changed_t', { 'handle' : dev.vif, 'status' : NETIF_INTERFACE_STATUS_CONNECTED, 'evtchn' : dev.evtchn['port2'], 'mac' : dev.mac }) - self.writeRequest(msg) + self.writeRequest(msg, response=response) - def send_be_create(self, vif): + def send_be_create(self, vif, response=None): dev = self.devices[vif] msg = packMsg('netif_be_create_t', { 'domid' : self.dom, 'netif_handle' : dev.vif, 'mac' : dev.mac }) - self.factory.writeRequest(msg) + self.factory.writeRequest(msg, response=response) - def send_be_disconnect(self, vif): + def send_be_disconnect(self, vif, response=None): dev = self.devices[vif] msg = packMsg('netif_be_disconnect_t', { 'domid' : self.dom, 'netif_handle' : dev.vif }) - self.factory.writeRequest(msg) + self.factory.writeRequest(msg, response=response) - def send_be_destroy(self, vif): - print 'NetifController>send_be_destroy>', 'dom=', self.dom, 'vif=', vif + def send_be_destroy(self, vif, response=None): PrettyPrint.prettyprint(self.sxpr()) dev = self.devices[vif] del self.devices[vif] msg = packMsg('netif_be_destroy_t', { 'domid' : self.dom, 'netif_handle' : vif }) - self.factory.writeRequest(msg) + self.factory.writeRequest(msg, response=response) diff --git a/tools/python/xen/xend/sxp.py b/tools/python/xen/xend/sxp.py index 143ba81ab4..352f7f2933 100644 --- a/tools/python/xen/xend/sxp.py +++ b/tools/python/xen/xend/sxp.py @@ -21,56 +21,19 @@ from StringIO import StringIO __all__ = [ "mime_type", "ParseError", - "Parser", - "atomp", - "show", - "show_xml", - "elementp", + "Parser", + "show", "name", - "attributes", - "attribute", "children", "child", - "child_at", - "child0", - "child1", - "child2", - "child3", - "child4", "child_value", - "has_id", - "with_id", - "child_with_id", - "elements", "to_string", "from_string", - "all_from_string", "parse", ] mime_type = "application/sxp" -escapes = { - 'a': '\a', - 'b': '\b', - 't': '\t', - 'n': '\n', - 'v': '\v', - 'f': '\f', - 'r': '\r', - '\\': '\\', - '\'': '\'', - '\"': '\"'} - -k_list_open = "(" -k_list_close = ")" -k_attr_open = "@" -k_eval = "!" - -escapes_rev = {} -for k in escapes: - escapes_rev[escapes[k]] = k - class ParseError(StandardError): def __init__(self, parser, value): @@ -86,7 +49,6 @@ class ParserState: self.parent = parent self.buf = '' self.val = [] - self.delim = None self.fn = fn def push(self, fn): @@ -95,28 +57,19 @@ class ParserState: class Parser: def __init__(self): - self.error = sys.stderr self.reset() def reset(self): - self.val = [] self.eof = 0 - self.err = 0 self.line_no = 0 self.char_no = 0 - self.state = None + self.state = self.start_state = ParserState(self.state_start) def push_state(self, fn): self.state = self.state.push(fn) def pop_state(self): - val = self.state self.state = self.state.parent - if self.state and self.state.fn == self.state_start: - # Return to start state - produce the value. - self.val += self.state.val - self.state.val = [] - return val def in_class(self, c, s): return s.find(c) >= 0 @@ -136,12 +89,6 @@ class Parser: def in_printable_class(self, c): return self.in_class(c, string.printable) - def set_error_stream(self, error): - self.error = error - - def has_error(self): - return self.err > 0 - def at_eof(self): return self.eof @@ -166,439 +113,131 @@ class Parser: self.char_no += 1 if self.state is None: - self.begin_start(None) + self.state = ParserState(self.state_start) + self.state.fn(c) def ready(self): - return len(self.val) > 0 + return len(self.start_state.val) > 0 def get_val(self): - v = self.val[0] - self.val = self.val[1:] + v = self.start_state.val[0] + self.start_state.val = self.start_state.val[1:] return v def get_all(self): - return self.val + return self.start_state.val - def begin_start(self, c): - self.state = ParserState(self.state_start) - - def end_start(self): - self.val += self.state.val - self.pop_state() - def state_start(self, c): - if self.at_eof(): - self.end_start() - elif self.in_space_class(c): + if self.at_eof() or self.in_space_class(c): pass elif self.in_comment_class(c): - self.begin_comment(c) - elif c == k_list_open: - self.begin_list(c) - elif c == k_list_close: + self.push_state(self.state_comment) + elif c == '(': + self.push_state(self.state_list) + elif c == ')': raise ParseError(self, "syntax error: "+c) elif self.in_string_quote_class(c): - self.begin_string(c) + self.push_state(self.state_string) + self.state.buf = c elif self.in_printable_class(c): - self.begin_atom(c) + self.push_state(self.state_atom) + self.state.buf = c elif c == chr(4): # ctrl-D, EOT: end-of-text. self.input_eof() else: raise ParseError(self, "invalid character: code %d" % ord(c)) - def begin_comment(self, c): - self.push_state(self.state_comment) - self.state.buf += c - - def end_comment(self): - self.pop_state() - def state_comment(self, c): if c == '\n' or self.at_eof(): - self.end_comment() - else: - self.state.buf += c + self.pop_state() - def begin_string(self, c): - self.push_state(self.state_string) - self.state.delim = c - - def end_string(self): - val = self.state.buf - self.state.parent.val.append(val) - self.pop_state() - def state_string(self, c): if self.at_eof(): raise ParseError(self, "unexpected EOF") - elif c == self.state.delim: - self.end_string() - elif c == '\\': - self.push_state(self.state_escape) - else: - self.state.buf += c - - def state_escape(self, c): - if self.at_eof(): - raise ParseError(self, "unexpected EOF") - d = escapes.get(c) - if d: - self.state.parent.buf += d + self.state.buf += c + # Look out for non-escaped end delimiter + if self.state.buf[0] == c and self.state.buf[-2] != '\\': + try: # parse escape sequences but fall back to something simple + val = eval(compile(self.state.buf,'','eval')) + except: + val = self.state.buf[1:-1] # just strip the delimiters + self.state.parent.val.append(val) self.pop_state() - elif c == 'x': - self.state.fn = self.state_hex - self.state.val = 0 - else: - self.state.fn = self.state_octal - self.state.val = 0 - self.input_char(c) - - def state_octal(self, c): - def octaldigit(c): - self.state.val *= 8 - self.state.val += ord(c) - ord('0') - self.state.buf += c - if self.state.val < 0 or self.state.val > 0xff: - raise ParseError(self, "invalid octal escape: out of range " + self.state.buf) - if len(self.state.buf) == 3: - octaldone() - - def octaldone(): - d = chr(self.state.val) - self.state.parent.buf += d - self.pop_state() - - if self.at_eof(): - raise ParseError(self, "unexpected EOF") - elif '0' <= c <= '7': - octaldigit(c) - elif len(self.buf): - octaldone() - self.input_char(c) - - def state_hex(self, c): - def hexdone(): - d = chr(self.state.val) - self.state.parent.buf += d - self.pop_state() - - def hexdigit(c, d): - self.state.val *= 16 - self.state.val += ord(c) - ord(d) - self.state.buf += c - if self.state.val < 0 or self.state.val > 0xff: - raise ParseError(self, "invalid hex escape: out of range " + self.state.buf) - if len(self.state.buf) == 2: - hexdone() - - if self.at_eof(): - raise ParseError(self, "unexpected EOF") - elif '0' <= c <= '9': - hexdigit(c, '0') - elif 'A' <= c <= 'F': - hexdigit(c, 'A') - elif 'a' <= c <= 'f': - hexdigit(c, 'a') - elif len(buf): - hexdone() - self.input_char(c) - - def begin_atom(self, c): - self.push_state(self.state_atom) - self.state.buf = c - - def end_atom(self): - val = self.state.buf - self.state.parent.val.append(val) - self.pop_state() def state_atom(self, c): - if self.at_eof(): - self.end_atom() - elif (self.is_separator(c) or - self.in_space_class(c) or - self.in_comment_class(c)): - self.end_atom() - self.input_char(c) + if (self.at_eof() or + self.is_separator(c) or + self.in_space_class(c) or + self.in_comment_class(c)): + val = self.state.buf + self.state.parent.val.append(val) + self.pop_state() + if not self.at_eof(): + self.input_char(c) else: self.state.buf += c - def begin_list(self, c): - self.push_state(self.state_list) - - def end_list(self): - val = self.state.val - self.state.parent.val.append(val) - self.pop_state() - def state_list(self, c): if self.at_eof(): raise ParseError(self, "unexpected EOF") - elif c == k_list_close: - self.end_list() + elif c == ')': + val = self.state.val + self.state.parent.val.append(val) + self.pop_state() else: self.state_start(c) -def atomp(sxpr): - """Check if an sxpr is an atom. - """ - if sxpr.isalnum() or sxpr == '@': - return 1 - for c in sxpr: - if c in string.whitespace: return 0 - if c in '"\'\\(){}[]<>$#&%^': return 0 - if c in string.ascii_letters: continue - if c in string.digits: continue - if c in '.-_:/~': continue - return 0 - return 1 - def show(sxpr, out=sys.stdout): """Print an sxpr in bracketed (lisp-style) syntax. """ if isinstance(sxpr, types.ListType): - out.write(k_list_open) - i = 0 + out.write('(') for x in sxpr: - if i: out.write(' ') show(x, out) - i += 1 - out.write(k_list_close) - elif isinstance(sxpr, types.StringType) and atomp(sxpr): - out.write(sxpr) + out.write(' ') + out.write(')') else: - #out.write("'" + str(sxpr) + "'") out.write(repr(str(sxpr))) -def show_xml(sxpr, out=sys.stdout): - """Print an sxpr in XML syntax. - """ - if isinstance(sxpr, types.ListType): - element = name(sxpr) - out.write('<%s' % element) - for attr in attributes(sxpr): - out.write(' %s=%s' % (attr[0], attr[1])) - out.write('>') - i = 0 - for x in children(sxpr): - if i: out.write(' ') - show_xml(x, out) - i += 1 - out.write('' % element) - elif isinstance(sxpr, types.StringType) and atomp(sxpr): - out.write(sxpr) - else: - out.write(str(sxpr)) - -def elementp(sxpr, elt=None): - """Check if an sxpr is an element of the given type. - - sxpr sxpr - elt element type - """ - return (isinstance(sxpr, types.ListType) - and len(sxpr) - and (None == elt or sxpr[0] == elt)) - def name(sxpr): - """Get the element name of an sxpr. - If the sxpr is not an element (i.e. it's an atomic value) its name - is None. - - sxpr - - returns name (None if not an element). + """Get the element name of an sxpr, or None if a bad sxpr. """ - val = None if isinstance(sxpr, types.StringType): - val = sxpr - elif isinstance(sxpr, types.ListType) and len(sxpr): - val = sxpr[0] - return val - -def attributes(sxpr): - """Get the attribute list of an sxpr. - - sxpr - - returns attribute list - """ - val = [] - if isinstance(sxpr, types.ListType) and len(sxpr) > 1: - attr = sxpr[1] - if elementp(attr, k_attr_open): - val = attr[1:] - return val - -def attribute(sxpr, key, val=None): - """Get an attribute of an sxpr. - - sxpr sxpr - key attribute key - val default value (default None) - - returns attribute value - """ - for x in attributes(sxpr): - if x[0] == key: - val = x[1] - break - return val + return sxpr + if isinstance(sxpr, types.ListType) and len(sxpr): + return sxpr[0] + return None def children(sxpr, elt=None): - """Get children of an sxpr. - - sxpr sxpr - elt optional element type to filter by - - returns children (filtered by elt if specified) + """Get children of an s-expression @sxpr, optionally filtered by + element type @elt. """ - val = [] - if isinstance(sxpr, types.ListType) and len(sxpr) > 1: - i = 1 - x = sxpr[i] - if elementp(x, k_attr_open): - i += 1 - val = sxpr[i : ] + if not isinstance(sxpr, types.ListType): return [] + val = filter(lambda x: isinstance(x, types.ListType) and len(x) > 0, sxpr) if elt: - def iselt(x): - return elementp(x, elt) - val = filter(iselt, val) + val = filter(lambda x,y=elt: x[0] == y, val) return val -def child(sxpr, elt, val=None): - """Get the first child of the given element type. - - sxpr sxpr - elt element type - val default value +def child(sxpr, elt=None, idx=0): + """Get the @idx'th child of the optional filtering type @elt in @sxpr. """ - for x in children(sxpr): - if elementp(x, elt): - val = x - break - return val + x = children(sxpr, elt) + if len(x) > idx: + return x[idx] + return None -def child_at(sxpr, index, val=None): - """Get the child at the given index (zero-based). - - sxpr sxpr - index index - val default value +def child_value(sxpr, elt=None): + """Get the value of the first child of @sxpr with the optional type @elt. """ - kids = children(sxpr) - if len(kids) > index: - val = kids[index] - return val - -def child0(sxpr, val=None): - """Get the zeroth child. - """ - return child_at(sxpr, 0, val) - -def child1(sxpr, val=None): - """Get the first child. - """ - return child_at(sxpr, 1, val) - -def child2(sxpr, val=None): - """Get the second child. - """ - return child_at(sxpr, 2, val) - -def child3(sxpr, val=None): - """Get the third child. - """ - return child_at(sxpr, 3, val) - -def child4(sxpr, val=None): - """Get the fourth child. - """ - return child_at(sxpr, 4, val) - -def child_value(sxpr, elt, val=None): - """Get the value of the first child of the given element type. - Assumes the child has an atomic value. - - sxpr sxpr - elt element type - val default value - """ - kid = child(sxpr, elt) - if kid: - val = child_at(kid, 0, val) - return val - -def has_id(sxpr, id): - """Test if an s-expression has a given id. - """ - return attribute(sxpr, 'id') == id - -def with_id(sxpr, id, val=None): - """Find the first s-expression with a given id, at any depth. - - sxpr s-exp or list - id id - val value if not found (default None) - - return s-exp or val - """ - if isinstance(sxpr, types.ListType): - for n in sxpr: - if has_id(n, id): - val = n - break - v = with_id(n, id) - if v is None: continue - val = v - break - return val - -def child_with_id(sxpr, id, val=None): - """Find the first child with a given id. - - sxpr s-exp or list - id id - val value if not found (default None) - - return s-exp or val - """ - if isinstance(sxpr, types.ListType): - for n in sxpr: - if has_id(n, id): - val = n - break - return val - -def elements(sxpr, ctxt=None): - """Generate elements (at any depth). - Visit elements in pre-order. - Values generated are (node, context) - The context is None if there is no parent, otherwise - (index, parent, context) where index is the node's index w.r.t its parent, - and context is the parent's context. - - sxpr s-exp - - returns generator - """ - yield (sxpr, ctxt) - i = 0 - for n in children(sxpr): - if isinstance(n, types.ListType): - # Calling elements() recursively does not generate recursively, - # it just returns a generator object. So we must iterate over it. - for v in elements(n, (i, sxpr, ctxt)): - yield v - i += 1 + x = child(sxpr, elt) + if not isinstance(x, types.ListType) or len(x) < 2: + return None + return x[1] def to_string(sxpr): - """Convert an sxpr to a string. - - sxpr sxpr - returns string + """Convert an s-expression @sxpr to a string. """ io = StringIO() show(sxpr, io) @@ -608,35 +247,14 @@ def to_string(sxpr): return val def from_string(str): - """Create an sxpr by parsing a string. - - str string - returns sxpr - """ - io = StringIO(str) - vals = parse(io) - if vals is []: - return None - else: - return vals[0] - - -def all_from_string(str): - """Create an sxpr list by parsing a string. - - str string - returns sxpr list + """Create an sxpr list from a given input string @str. """ io = StringIO(str) vals = parse(io) return vals def parse(io): - """Completely parse all input from 'io'. - - io input file object - returns list of values, None if incomplete - raises ParseError on parse error + """Completely parse all input from file @io. """ pin = Parser() while 1: @@ -649,19 +267,11 @@ def parse(io): else: val = None return val + - if __name__ == '__main__': - print ">main" pin = Parser() - while 1: - buf = sys.stdin.read(1024) - #buf = sys.stdin.readline() - pin.input(buf) - while pin.ready(): - val = pin.get_val() - print - print '****** val=', val - if len(buf) == 0: - break - + buf = sys.stdin.read(1024) + pin.input(buf) + while pin.ready(): + print '\n****** val=', pin.get_val() diff --git a/tools/python/xen/xm/create.py b/tools/python/xen/xm/create.py index 7819243adb..cf82d075f9 100644 --- a/tools/python/xen/xm/create.py +++ b/tools/python/xen/xm/create.py @@ -1,4 +1,5 @@ # Copyright (C) 2004 Mike Wray + """Domain creation. """ import string @@ -12,15 +13,25 @@ from xen.util import console_client from xen.xm.opts import * -gopts = Opts(use="""[options] +gopts = Opts(use="""[options] [vars] Create a domain. + +Domain creation parameters can be set by command-line switches, from +a python configuration script or an SXP config file. See documentation +for --defaults, --config. Configuration variables can be set using +VAR=VAL on the command line. For example vmid=3 sets vmid to 3. + """) gopts.opt('help', short='h', fn=set_true, default=0, use="Print this help.") +gopts.opt('help_config', + fn=set_true, default=0, + use="Print help for configuration file.") + gopts.opt('quiet', short='q', fn=set_true, default=0, use="Quiet.") @@ -31,58 +42,73 @@ gopts.opt('path', val='PATH', gopts.opt('defaults', short='f', val='FILE', fn=set_value, default='xmdefaults', - use="Use the given default script.") + use="""Use the given Python defaults script. +The defaults script is loaded after arguments have been processed. +Each command-line option sets a configuration variable named after +its long option name, and these variables are placed in the +environment of the script before it is loaded. +Variables for options that may be repeated have list values. +Other variables can be set using VAR=VAL on the command line. + +After the script is loaded, option values that were not set on the +command line are replaced by the values set in the script. +""") gopts.opt('config', short='F', val='FILE', fn=set_value, default=None, - use='Domain configuration to use (SXP).') + use="""Domain configuration to use (SXP). +SXP is the underlying configuration format used by Xen. +SXP configs can be hand-written or generated from Python defaults +scripts, using the -n (dryrun) option to print the config. +""") gopts.opt('load', short='L', val='FILE', fn=set_value, default=None, use='Domain saved state to load.') -gopts.opt('define', short='D', val='VAR=VAL', - fn=set_var, default=None, - use="""Set a variable before loading defaults, e.g. '-D vmid=3' - to set vmid. May be repeated to set more thanone variable.""") - gopts.opt('dryrun', short='n', fn=set_true, default=0, - use="Dry run - print the config but don't create the domain.") - -gopts.opt('name', short='N', val='NAME', - fn=set_value, default=None, - use="Domain name.") + use="""Dry run - print the config but don't create the domain. +The defaults file is loaded and the SXP configuration is created and printed. +""") gopts.opt('console_autoconnect', short='c', fn=set_true, default=0, use="Connect to console after domain is created.") -gopts.opt('kernel', short='k', val='FILE', +gopts.var('name', val='NAME', + fn=set_value, default=None, + use="Domain name.") + +gopts.var('kernel', val='FILE', fn=set_value, default=None, use="Path to kernel image.") -gopts.opt('ramdisk', short='r', val='FILE', +gopts.var('ramdisk', val='FILE', fn=set_value, default='', use="Path to ramdisk.") -gopts.opt('builder', short='b', val='FUNCTION', +gopts.var('builder', val='FUNCTION', fn=set_value, default='linux', use="Function to use to build the domain.") -gopts.opt('memory', short='m', val='MEMORY', +gopts.var('memory', val='MEMORY', fn=set_value, default=128, use="Domain memory in MB.") -gopts.opt('blkif', - fn=set_true, default=0, +gopts.var('autorestart', val='no|yes', + fn=set_bool, default=0, + use="Whether to restart the domain on exit.") + +gopts.var('blkif', val='no|yes', + fn=set_bool, default=0, use="Make the domain a block device backend.") -gopts.opt('netif', - fn=set_true, default=0, +gopts.var('netif', val='no|yes', + fn=set_bool, default=0, use="Make the domain a network interface backend.") -gopts.opt('disk', short='d', val='phy:DEV,VDEV,MODE', +gopts.var('disk', val='phy:DEV,VDEV,MODE', fn=append_value, default=[], use="""Add a disk device to a domain. The physical device is DEV, which is exported to the domain as VDEV. The disk is read-only if MODE @@ -90,18 +116,18 @@ gopts.opt('disk', short='d', val='phy:DEV,VDEV,MODE', The option may be repeated to add more than one disk. """) -gopts.opt('pci', val='BUS,DEV,FUNC', +gopts.var('pci', val='BUS,DEV,FUNC', fn=append_value, default=[], use="""Add a PCI device to a domain, using given params (in hex). For example '-pci c0,02,1a'. The option may be repeated to add more than one pci device. """) -gopts.opt('ipaddr', short='i', val="IPADDR", +gopts.var('ipaddr', val="IPADDR", fn=append_value, default=[], use="Add an IP address to the domain.") -gopts.opt('vif', val="mac=MAC,bridge=BRIDGE", +gopts.var('vif', val="mac=MAC,bridge=BRIDGE", fn=append_value, default=[], use="""Add a network interface with the given MAC address and bridge. If mac is not specified a random MAC address is used. @@ -110,7 +136,7 @@ gopts.opt('vif', val="mac=MAC,bridge=BRIDGE", Specifying vifs will increase the number of interfaces as needed. """) -gopts.opt('nics', val="NUM", +gopts.var('nics', val="NUM", fn=set_int, default=1, use="""Set the number of network interfaces. Use the vif option to define interface parameters, otherwise @@ -118,44 +144,44 @@ gopts.opt('nics', val="NUM", number of interfaces as needed. """) -gopts.opt('root', short='R', val='DEVICE', +gopts.var('root', val='DEVICE', fn=set_value, default='', use="""Set the root= parameter on the kernel command line. Use a device, e.g. /dev/sda1, or /dev/nfs for NFS root.""") -gopts.opt('extra', short='E', val="ARGS", +gopts.var('extra', val="ARGS", fn=set_value, default='', use="Set extra arguments to append to the kernel command line.") -gopts.opt('ip', short='I', val='IPADDR', +gopts.var('ip', val='IPADDR', fn=set_value, default='', use="Set the kernel IP interface address.") -gopts.opt('gateway', val="IPADDR", +gopts.var('gateway', val="IPADDR", fn=set_value, default='', use="Set the kernel IP gateway.") -gopts.opt('netmask', val="MASK", +gopts.var('netmask', val="MASK", fn=set_value, default = '', use="Set the kernel IP netmask.") -gopts.opt('hostname', val="NAME", +gopts.var('hostname', val="NAME", fn=set_value, default='', use="Set the kernel IP hostname.") -gopts.opt('interface', val="INTF", +gopts.var('interface', val="INTF", fn=set_value, default="eth0", use="Set the kernel IP interface name.") -gopts.opt('dhcp', val="off|dhcp", +gopts.var('dhcp', val="off|dhcp", fn=set_value, default='off', use="Set the kernel dhcp option.") -gopts.opt('nfs_server', val="IPADDR", +gopts.var('nfs_server', val="IPADDR", fn=set_value, default=None, use="Set the address of the NFS server for NFS root.") -gopts.opt('nfs_root', val="PATH", +gopts.var('nfs_root', val="PATH", fn=set_value, default=None, use="Set the path of the root NFS directory.") @@ -244,6 +270,8 @@ def make_config(opts): config.append(['backend', ['blkif']]) if opts.netif: config.append(['backend', ['netif']]) + if opts.autorestart: + config.append(['autorestart']) configure_image(config, opts) config_devs = [] @@ -282,7 +310,7 @@ def preprocess_vifs(opts): d = {} a = vif.split(',') for b in a: - (k, v) = b.strip().split('=') + (k, v) = b.strip().split('=', 1) k = k.strip() v = v.strip() if k not in ['mac', 'bridge']: @@ -352,7 +380,15 @@ def main(argv): args = opts.parse(argv) if opts.vals.help: opts.usage() + if opts.vals.help or opts.vals.help_config: + opts.load_defaults(help=1) + if opts.vals.help or opts.vals.help_config: return + # Process remaining args as config variables. + for arg in args: + if '=' in arg: + (var, val) = arg.strip().split('=', 1) + gopts.setvar(var.strip(), val.strip()) if opts.vals.config: pass else: diff --git a/tools/python/xen/xm/help.py b/tools/python/xen/xm/help.py new file mode 100644 index 0000000000..88df4412c0 --- /dev/null +++ b/tools/python/xen/xm/help.py @@ -0,0 +1,82 @@ +# Copyright (C) 2004 Mike Wray + +"""Variable definition and help support for Python defaults files. +""" + +import sys + +class Vars: + """A set of configuration variables. + """ + + def __init__(self, name, help, env): + """Create a variable set. + + name name of the defaults file + help help flag + env local environment + """ + self.name = name + self.help = help + self.env = env + self.vars = [] + + def var(self, name, use=None, check=None): + """Define a configuration variable. + If provided, the check function will be called as check(var, val) + where var is the variable name and val is its value (string). + It should return a new value for the variable, or raise ValueError if + the value is not acceptable. + + name variable name + use variable usage string + check variable check function + """ + self.vars.append(Var(name, use, check)) + + def check(self): + """Execute the variable checks or print help, depending on the value + of the help flag passed to the constructor. + """ + if self.help: + self.doHelp() + else: + for v in self.vars: + v.doCheck(self.env) + + def doHelp(self, out=sys.stderr): + """Print help for the variables. + """ + if self.vars: + print >>out, "\nConfiguration variables for %s:\n" % self.name + for v in self.vars: + v.doHelp(out) + print >>out + +class Var: + """A single variable. + """ + + def __init__(self, name, use, check): + """Create a variable. + + name variable name + use variable use string + check variable value check function + """ + self.name = name + self.use = use or '' + self.check = check + + def doCheck(self, env): + """Execute the check and set the variable to the new value. + """ + if not self.check: return + env[self.name] = self.check(self.name, env.get(self.name)) + + def doHelp(self, out): + """Print help for the variable. + """ + print >>out, "%-12s" % self.name, self.use + + diff --git a/tools/python/xen/xm/main.py b/tools/python/xen/xm/main.py index 3ab5d23cf7..9ae1fb67b8 100644 --- a/tools/python/xen/xm/main.py +++ b/tools/python/xen/xm/main.py @@ -5,10 +5,12 @@ import os import os.path import sys from getopt import getopt +import socket from xen.xend import PrettyPrint from xen.xend import sxp from xen.xend.XendClient import server +from xen.xend.XendClient import main as xend_client_main from xen.xm import create, shutdown class Prog: @@ -65,6 +67,13 @@ class Xm: sys.exit(1) def main(self, args): + try: + self.main_call(args) + except socket.error, ex: + print >>sys.stderr, ex + self.err("Error connecting to xend, is xend running?") + + def main_call(self, args): """Main entry point. Dispatches to the progs. """ self.name = args[0] @@ -237,11 +246,11 @@ class ProgList(Prog): info = server.xend_domain(dom) d = {} d['dom'] = int(dom) - d['name'] = sxp.child_value(info, 'name', '??') - d['mem'] = int(sxp.child_value(info, 'memory', '0')) - d['cpu'] = int(sxp.child_value(info, 'cpu', '0')) - d['state'] = sxp.child_value(info, 'state', '??') - d['cpu_time'] = float(sxp.child_value(info, 'cpu_time', '0')) + d['name'] = sxp.child_value(info, 'name') or '??' + d['mem'] = int(sxp.child_value(info, 'memory') or '-1') + d['cpu'] = int(sxp.child_value(info, 'cpu') or '-1') + d['state'] = sxp.child_value(info, 'state') or '??' + d['cpu_time'] = float(sxp.child_value(info, 'cpu_time') or '-1') print ("%(dom)-4d %(name)-16s %(mem)7d %(cpu)3d %(state)5s %(cpu_time)7.1f" % d) def long_list(self, doms): @@ -415,9 +424,9 @@ class ProgConsoles(Prog): for x in l: info = server.xend_console(x) d = {} - d['dom'] = sxp.child(info, 'dst', ['dst', '?', '?'])[1] - d['port'] = sxp.child_value(info, 'port', '?') - d['id'] = sxp.child_value(info, 'id', '?') + d['dom'] = (sxp.child(info, 'dst') or ['dst', '?', '?'])[1] + d['port'] = sxp.child_value(info, 'port') or '?' + d['id'] = sxp.child_value(info, 'id') or '?' print "%(dom)3s %(port)4s %(id)3s" % d xm.prog(ProgConsoles) @@ -444,5 +453,21 @@ class ProgConsole(Prog): xm.prog(ProgConsole) +class ProgCall(Prog): + name = "call" + info = "Call xend api functions." + + def help (self, args): + print "call fn argss..." + print """ + Call a xend HTTP API function. The leading 'xend_' on the function +can be omitted. See xen.xend.XendClient for the API functions. +""" + + def main(self, args): + xend_client_main(args) + +xm.prog(ProgCall) + def main(args): xm.main(args) diff --git a/tools/python/xen/xm/opts.py b/tools/python/xen/xm/opts.py index eb07936f1e..325698875d 100644 --- a/tools/python/xen/xm/opts.py +++ b/tools/python/xen/xm/opts.py @@ -126,6 +126,42 @@ class Opt: """ return self.specified_opt +class OptVar(Opt): + """An individual option variable. + """ + def __init__(self, opts, name, + val=None, fn=None, use=None, default=None): + """Create an option. + + opts parent options object + name name of the field it controls + val string used to print option args in help. + If val is not specified the option has no arg. + fn function to call when the option is specified. + use usage (help) string + default default value if not specified on command-line + """ + if val is None: + val = name.upper() + Opt.__init__(self, opts, name, val=val, fn=fn, use=use, default=default) + self.optkeys = [] + self.optkeys.append(self.long) + + def short_opt(self): + return None + + def long_opt(self): + return None + + def show(self): + print '%s=%s' %(self.optkeys[0], self.val) + print + if self.use: + print '\t', + print self.use + if self.val: + print '\tDefault', self.default or 'None' + class OptVals: """Class to hold option values. """ @@ -134,6 +170,13 @@ class OptVals: class Opts: """Container for options. """ + + imports = ["import sys", + "import os", + "import os.path", + "from xen.util.ip import *", + ] + def __init__(self, use=None): """Options constructor. @@ -168,6 +211,12 @@ class Opts: self.options_map[name] = x return x + def var(self, name, **args): + x = OptVar(self, name, **args) + self.options.append(x) + self.options_map[name] = x + return x + def setvar(self, var, val): """Set a default script variable. """ @@ -232,7 +281,18 @@ class Opts: else: print >>sys.stderr, "Error: Unknown option:", k self.usage() - return args + xargs = [] + for arg in args: + isvar = 0 + if '=' in arg: + (k, v) = arg.split('=', 1) + for opt in self.options: + if opt.specify(k, v): + isvar = 1 + break + if not isvar: + xargs.append(arg) + return xargs def short_opts(self): """Get short options specifier for getopt. @@ -257,9 +317,10 @@ class Opts: def usage(self): print 'Usage: ', self.argv[0], self.use or 'OPTIONS' for opt in self.options: + print opt.show() - def load_defaults(self): + def load_defaults(self, help=0): """Load a defaults script. Assumes these options set: 'path' search path 'default' script name @@ -270,12 +331,12 @@ class Opts: else: p = self.vals.defaults if os.path.exists(p): - self.load(p) + self.load(p, help) break else: self.err("Cannot open defaults file %s" % self.vals.defaults) - def load(self, defaults, help=0): + def load(self, defaults, help): """Load a defaults file. Local variables in the file are used to set options with the same names. Variables are not used to set options that are already specified. @@ -287,15 +348,22 @@ class Opts: globals = {} locals = {} locals.update(self.vars) - cmd = '\n'.join(["import sys", - "import os", - "import os.path", - "import xen.util.ip", - "xm_file = '%s'" % defaults, - "xm_help = %d" % help ]) + cmd = '\n'.join(self.imports + + [ "from xen.xm.help import Vars", + "xm_file = '%s'" % defaults, + "xm_help = %d" % help, + "xm_vars = Vars(xm_file, xm_help, locals())" + ]) exec cmd in globals, locals - execfile(defaults, globals, locals) - if help: return + try: + execfile(defaults, globals, locals) + except: + if not help: raise + if help: + print 'The following imports are done automatically:' + for x in self.imports: + print x + return # Extract the values set by the script and set the corresponding # options, if not set on the command line. vtypes = [ types.StringType, @@ -316,6 +384,17 @@ def set_false(opt, k, v): """Set an option false.""" opt.set(0) +def set_bool(opt, k, v): + """Set a boolean option. + """ + if v in ['yes']: + opt.set(1) + elif v in ['no']: + opt.set(0) + else: + opt.opts.err('Invalid value:' +v) + + def set_value(opt, k, v): """Set an option to a valoue.""" opt.set(v) @@ -335,6 +414,6 @@ def append_value(opt, k, v): def set_var(opt, k, v): """Set a default script variable. """ - (var, val) = v.strip().split('=') + (var, val) = v.strip().split('=', 1) opt.opts.setvar(var.strip(), val.strip()) diff --git a/tools/python/xen/xm/shutdown.py b/tools/python/xen/xm/shutdown.py index aaa354554a..926aaf27f3 100644 --- a/tools/python/xen/xm/shutdown.py +++ b/tools/python/xen/xm/shutdown.py @@ -1,4 +1,5 @@ # Copyright (C) 2004 Mike Wray + """Domain shutdown. """ import string @@ -10,7 +11,8 @@ from xen.xm.opts import * gopts = Opts(use="""[options] [DOM] -Shutdown one or more domains gracefully.""") +Shutdown one or more domains gracefully. +""") gopts.opt('help', short='h', fn=set_true, default=0, @@ -24,18 +26,22 @@ gopts.opt('wait', short='w', fn=set_true, default=0, use='Wait for shutdown to complete.') -gopts.opt('norestart', short='n', +gopts.opt('halt', short='H', fn=set_true, default=0, - use='Prevent domain restart.') + use='Shutdown without reboot.') -def shutdown(opts, doms, wait): +gopts.opt('reboot', short='R', + fn=set_true, default=0, + use='Shutdown and reboot.') + +def shutdown(opts, doms, mode, wait): def domains(): return [ int(a) for a in server.xend_domains() ] if doms == None: doms = domains() if 0 in doms: doms.remove(0) for d in doms: - server.xend_domain_shutdown(d) + server.xend_domain_shutdown(d, mode) if wait: while doms: alive = domains() @@ -49,6 +55,21 @@ def shutdown(opts, doms, wait): time.sleep(1) opts.info("All domains terminated") +def shutdown_mode(opts): + mode = 'poweroff' + if opts.vals.wait: + mode = 'halt' + if opts.vals.reboot: + opts.err("Can't specify wait and reboot") + else: + if opts.vals.halt and opts.vals.reboot: + opts.err("Can't specify halt and reboot") + if opts.vals.halt: + mode = 'halt' + elif opts.vals.reboot: + mode = 'reboot' + return mode + def main_all(opts, args): shutdown(opts, None, opts.vals.wait) @@ -59,7 +80,9 @@ def main_dom(opts, args): domid = int(dom) except: opts.err('Invalid domain: ' + dom) - shutdown(opts, [ domid ], opts.vals.wait) + + mode = shutdown_mode(opts) + shutdown(opts, [ domid ], mode, opts.vals.wait) def main(argv): opts = gopts diff --git a/tools/xfrd/Make.xfrd b/tools/xfrd/Make.xfrd new file mode 100644 index 0000000000..39f3afd3c5 --- /dev/null +++ b/tools/xfrd/Make.xfrd @@ -0,0 +1,34 @@ +# -*- mode: Makefile; -*- +#============================================================================ + +UTIL_LIB = libutil.a + +UTIL_LIB_SRC = +UTIL_LIB_SRC += allocate.c +UTIL_LIB_SRC += enum.c +UTIL_LIB_SRC += file_stream.c +UTIL_LIB_SRC += gzip_stream.c +UTIL_LIB_SRC += hash_table.c +UTIL_LIB_SRC += iostream.c +UTIL_LIB_SRC += lexis.c +UTIL_LIB_SRC += lzi_stream.c +UTIL_LIB_SRC += marshal.c +UTIL_LIB_SRC += string_stream.c +UTIL_LIB_SRC += sxpr.c +#UTIL_LIB_SRC += sxpr_parser.c +UTIL_LIB_SRC += sys_net.c +UTIL_LIB_SRC += sys_string.c +#UTIL_LIB_SRC += util.c +UTIL_LIB_SRC += xdr.c + +#---------------------------------------------------------------------------- +# Xfrd. + +XFRD_PROG_SRC = +XFRD_PROG_SRC += xfrd.c +#XFRD_PROG_SRC += xfr_msg.c +XFRD_PROG_SRC += xen_domain.c +XFRD_PROG_SRC += select.c +XFRD_PROG_SRC += connection.c + +#============================================================================ diff --git a/tools/xfrd/Makefile b/tools/xfrd/Makefile new file mode 100644 index 0000000000..fe84120471 --- /dev/null +++ b/tools/xfrd/Makefile @@ -0,0 +1,68 @@ +# -*- mode: Makefile; -*- +#============================================================================ +# +# Mike Wray +#============================================================================ + +XEN_ROOT = ../.. +include $(XEN_ROOT)/tools/Make.defs + +XFRD_INSTALL_DIR = /usr/sbin + +vpath %.h $(XEN_HYPERVISOR_IFS) +INCLUDES += -I $(XEN_HYPERVISOR_IFS) + +vpath %h $(XEN_LINUX_INCLUDE) +INCLUDES += -I $(XEN_LINUX_INCLUDE) + +vpath %.h $(XEN_XU) +INCLUDES += -I $(XEN_XU) + +vpath %c $(XEN_LIBXUTIL) +INCLUDES += -I $(XEN_LIBXUTIL) + +include Make.xfrd + +UTIL_LIB_OBJ = $(UTIL_LIB_SRC:.c=.o) + +XFRD_PROG_OBJ = $(XFRD_PROG_SRC:.c=.o) +XFRD_PROG_OBJ += $(UTIL_LIB) + +CPPFLAGS += -D _XEN_XFR_STUB_ + +CFLAGS += -g +CFLAGS += -Wall +CFALGS += -Werror +CFLAGS += $(INCLUDES) +# Make gcc generate dependencies. +CFLAGS += -Wp,-MD,.$(@F).d +PROG_DEP = .*.d + +#LDFLAGS += -L $(COMPRESS_DIR) -lz + +$(warning XFRD_PROG_OBJ= $(XFRD_PROG_OBJ)) +$(warning UTIL_LIB= $(UTIL_LIB)) +$(warning UTIL_LIB_OBJ= $(UTIL_LIB_OBJ)) + +all: xfrd + +xfrd: $(XFRD_PROG_OBJ) -lz + +.PHONY: install +install: xfrd + mkdir -p $(prefix)/$(XFRD_INSTALL_DIR) + install -m 0755 xfrd $(prefix)/$(XFRD_INSTALL_DIR) + +.PHONY: libutil +libutil: $(UTIL_LIB) + +$(UTIL_LIB): $(UTIL_LIB_OBJ) + $(AR) rc $@ $^ + +.PHONY: clean +clean: + $(RM) *.o *.a *.so *~ xfrd + $(RM) $(PROG_DEP) + +-include $(PROG_DEP) + diff --git a/tools/xfrd/connection.c b/tools/xfrd/connection.c new file mode 100644 index 0000000000..26dca9968b --- /dev/null +++ b/tools/xfrd/connection.c @@ -0,0 +1,163 @@ +#include +#include +#include +#include +#include +#include + +#include "connection.h" +#include "file_stream.h" +#include "lzi_stream.h" + +#define dprintf(fmt, args...) fprintf(stdout, "[DEBUG] %s" fmt, __FUNCTION__, ##args) +#define wprintf(fmt, args...) fprintf(stderr, "[WARN] %s" fmt, __FUNCTION__, ##args) +#define iprintf(fmt, args...) fprintf(stdout, "[INFO] %s" fmt, __FUNCTION__, ##args) +#define eprintf(fmt, args...) fprintf(stderr, "[ERROR] %s" fmt, __FUNCTION__, ##args) + +/** Compress magic header. */ +char compress_magic[2] = { 0x1f, 0x8b }; + +/** Plain magic header. */ +char plain_magic[2] = { 0x0, 0x0 }; + +int Conn_read_header(int sock, int *flags){ + int err = 0; + char magic[2] = {}; + int k, n = sizeof(magic); + k = read(sock, magic, n); + if(k != n){ + err = -EINVAL; + goto exit; + } + dprintf("> magic={ 0x%x, 0x%x }\n", magic[0], magic[1]); + if(magic[0] == compress_magic[0] && magic[1] == compress_magic[1]){ + *flags |= CONN_READ_COMPRESS; + dprintf("> Using compress read.\n"); + } else { + dprintf("> Using plain read.\n"); + } + exit: + return err; +} + +int Conn_write_header(int sock, int flags){ + int err = 0; + if(flags & CONN_WRITE_COMPRESS){ + dprintf("> Using compress write.\n"); + err = write(sock, compress_magic, 2); + } else { + dprintf("> Using plain write.\n"); + err = write(sock, plain_magic, 2); + } + if(err == 2) err = 0; + return err; +} + +/** Initialize a file stream from a file desciptor. + * + * @param fd file descriptor + * @param mode file mode + * @param flags control compression and buffering + * @param io return parameter for the stream + * @return 0 on success, error code otherwise + */ +int stream_init(int fd, const char *mode, int flags, int compress, IOStream **io){ + int err = 0; + dprintf(">mode=%s flags=%x compress=%d\n", mode, flags, compress); + if(compress){ + *io = lzi_stream_fdopen(fd, mode); + } else { + *io = file_stream_fdopen(fd, mode); + } + if(!*io){ + err = -errno; + perror("fdopen"); + goto exit; + } + if(1 && (flags & CONN_NOBUFFER)){ + // Make unbuffered. + dprintf("> unbuffer...\n"); + err = file_stream_setvbuf((compress ? lzi_stream_io(*io) : *io), NULL, _IONBF, 0); + if(err){ + err = -errno; + perror("setvbuf"); + goto exit; + } + } + exit: + if(err && *io){ + dprintf("> close err=%d\n", err); + IOStream_close(*io); + *io = NULL; + } + dprintf("< err=%d\n", err); + return err; +} + +/** Initialize a connection. + * + * @param conn connection + * @param flags + * @param sock socket + * @param ipaddr ip address + * @return 0 on success, error code otherwise + */ +int Conn_init(Conn *conn, int flags, int sock, struct sockaddr_in addr){ + int err = 0; + dprintf("> flags=%x\n", flags); + conn->addr = addr; + conn->sock = sock; + dprintf("> write stream...\n"); + err = stream_init(sock, "w", flags, (flags & CONN_WRITE_COMPRESS), &conn->out); + if(err) goto exit; + IOStream_flush(conn->out); + dprintf("> read stream...\n"); + err = stream_init(sock, "r", flags, (flags & CONN_READ_COMPRESS) , &conn->in); + if(err) goto exit; + exit: + if(err) eprintf("< err=%d\n", err); + return err; +} + +/** Open a connection. + * + * @param conn connection + * @param flags + * @param ipaddr ip address to connect to + * @param port port + * @return 0 on success, error code otherwise + */ +int Conn_connect(Conn *conn, int flags, struct in_addr ipaddr, uint16_t port){ + int err = 0; + int sock; + struct sockaddr_in addr_in; + struct sockaddr *addr = (struct sockaddr *)&addr_in; + socklen_t addr_n = sizeof(addr_in); + dprintf("> addr=%s:%d\n", inet_ntoa(ipaddr), ntohs(port)); + sock = socket(AF_INET, SOCK_STREAM, 0); + if(sock < 0){ + err = -errno; + goto exit; + } + addr_in.sin_family = AF_INET; + addr_in.sin_addr = ipaddr; + addr_in.sin_port = port; + err = connect(sock, addr, addr_n); + if(err) goto exit; + //err = Conn_write_header(sock, flags); + //if(err < 0) goto exit; + err = Conn_init(conn, flags, sock, addr_in); + exit: + if(err) eprintf("< err=%d\n", err); + return err; +} + +/** Close a connection. + * + * @param conn connection + */ +void Conn_close(Conn *conn){ + if(conn->in) IOStream_close(conn->in); + if(conn->out) IOStream_close(conn->out); + shutdown(conn->sock, 2); +} diff --git a/tools/xfrd/connection.h b/tools/xfrd/connection.h new file mode 100644 index 0000000000..790c01f1e8 --- /dev/null +++ b/tools/xfrd/connection.h @@ -0,0 +1,32 @@ +/* $Id: connection.h,v 1.1 2003/10/17 15:48:43 mjw Exp $ */ +#ifndef _VFC_CONNECTION_H_ +#define _VFC_CONNECTION_H_ + +#include + +#include "iostream.h" + +/** A connection. + * The underlying transport is a socket. + * Contains in and out streams using the socket. + */ +typedef struct Conn { + struct sockaddr_in addr; + int sock; + IOStream *in; + IOStream *out; +} Conn; + +enum { + CONN_NOBUFFER=1, + CONN_READ_COMPRESS=2, + CONN_WRITE_COMPRESS=4, +}; + +extern int Conn_read_header(int sock, int *flags); +extern int Conn_write_header(int sock, int flags); +extern int Conn_init(Conn *conn, int flags, int sock, struct sockaddr_in addr); +extern int Conn_connect(Conn *conn, int flags, struct in_addr ipaddr, uint16_t port); +extern void Conn_close(Conn *conn); + +#endif /* ! _VFC_CONNECTION_H_ */ diff --git a/tools/xfrd/debug.h b/tools/xfrd/debug.h new file mode 100644 index 0000000000..69a6b49937 --- /dev/null +++ b/tools/xfrd/debug.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2004 Mike Wray + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef _XUTIL_DEBUG_H_ +#define _XUTIL_DEBUG_H_ + +#ifndef MODULE_NAME +#define MODULE_NAME "" +#endif + +#ifdef __KERNEL__ +#include +#include + +#ifdef DEBUG + +#define dprintf(fmt, args...) printk(KERN_DEBUG "[DBG] " MODULE_NAME ">%s" fmt, __FUNCTION__, ##args) +#define wprintf(fmt, args...) printk(KERN_WARNING "[WRN] " MODULE_NAME ">%s" fmt, __FUNCTION__, ##args) +#define iprintf(fmt, args...) printk(KERN_INFO "[INF] " MODULE_NAME ">%s" fmt, __FUNCTION__, ##args) +#define eprintf(fmt, args...) printk(KERN_ERR "[ERR] " MODULE_NAME ">%s" fmt, __FUNCTION__, ##args) + +#else + +#define dprintf(fmt, args...) do {} while(0) +#define wprintf(fmt, args...) printk(KERN_WARNING "[WRN] " MODULE_NAME fmt, ##args) +#define iprintf(fmt, args...) printk(KERN_INFO "[INF] " MODULE_NAME fmt, ##args) +#define eprintf(fmt, args...) printk(KERN_ERR "[ERR] " MODULE_NAME fmt, ##args) + +#endif + +#else + +#include + +#ifdef DEBUG + +#define dprintf(fmt, args...) fprintf(stdout, "[DBG] " MODULE_NAME ">%s" fmt, __FUNCTION__, ##args) +#define wprintf(fmt, args...) fprintf(stderr, "[WRN] " MODULE_NAME ">%s" fmt, __FUNCTION__, ##args) +#define iprintf(fmt, args...) fprintf(stderr, "[INF] " MODULE_NAME ">%s" fmt, __FUNCTION__, ##args) +#define eprintf(fmt, args...) fprintf(stderr, "[ERR] " MODULE_NAME ">%s" fmt, __FUNCTION__, ##args) + +#else + +#define dprintf(fmt, args...) do {} while(0) +#define wprintf(fmt, args...) fprintf(stderr, "[WRN] " MODULE_NAME fmt, ##args) +#define iprintf(fmt, args...) fprintf(stderr, "[INF] " MODULE_NAME fmt, ##args) +#define eprintf(fmt, args...) fprintf(stderr, "[ERR] " MODULE_NAME fmt, ##args) + +#endif + +#endif + +/** Print format for an IP address. + * See NIPQUAD(), HIPQUAD() + */ +#define IPFMT "%u.%u.%u.%u" + +#endif /* ! _XUTIL_DEBUG_H_ */ diff --git a/tools/xfrd/enum.c b/tools/xfrd/enum.c new file mode 100644 index 0000000000..95f6e31a87 --- /dev/null +++ b/tools/xfrd/enum.c @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2002, 2004 Mike Wray + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. This library is + * distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef __KERNEL__ +#include +#else +#include +#endif + +#include "sys_string.h" +#include "enum.h" + +/** Map an enum name to its value using a table. + * + * @param name enum name + * @param defs enum definitions + * @return enum value or -1 if not known + */ +int enum_name_to_val(char *name, EnumDef *defs){ + int val = -1; + for(; defs->name; defs++){ + if(!strcmp(defs->name, name)){ + val = defs->val; + break; + } + } + return val; +} + +/** Map an enum value to its name using a table. + * + * @param val enum value + * @param defs enum definitions + * @param defs_n number of definitions + * @return enum name or NULL if not known + */ +char *enum_val_to_name(int val, EnumDef *defs){ + char *name = NULL; + for(; defs->name; defs++){ + if(val == defs->val){ + name = defs->name; + break; + } + } + return name; +} + diff --git a/tools/xfrd/enum.h b/tools/xfrd/enum.h new file mode 100644 index 0000000000..cdc0f6f1b1 --- /dev/null +++ b/tools/xfrd/enum.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2002, 2004 Mike Wray + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. This library is + * distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _XUTIL_ENUM_H_ +#define _XUTIL_ENUM_H_ + +/** Mapping of an enum value to a name. */ +typedef struct EnumDef { + int val; + char *name; +} EnumDef; + +extern int enum_name_to_val(char *name, EnumDef *defs); +extern char *enum_val_to_name(int val, EnumDef *defs); + +#endif /* _XUTIL_ENUM_H_ */ diff --git a/tools/xfrd/hash_table.c b/tools/xfrd/hash_table.c new file mode 100644 index 0000000000..13da946e77 --- /dev/null +++ b/tools/xfrd/hash_table.c @@ -0,0 +1,640 @@ +/* + * Copyright (C) 2001 - 2004 Mike Wray + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef __KERNEL__ +# include +# include +# include +# include +#else +# include +# include +#endif + +//#include + +#include "allocate.h" +#include "hash_table.h" + +/** @file + * Base support for hashtables. + * + * Hash codes are reduced modulo the number of buckets to index tables, + * so there is no need for hash functions to limit the range of hashcodes. + * In fact it is assumed that hashcodes do not change when the number of + * buckets in the table changes. + */ + +/*==========================================================================*/ +/** Number of bits in half a word. */ +//#if __WORDSIZE == 64 +//#define HALF_WORD_BITS 32 +//#else +#define HALF_WORD_BITS 16 +//#endif + +/** Mask for lo half of a word. On 32-bit this is + * (1<<16) - 1 = 65535 = 0xffff + * It's 4294967295 = 0xffffffff on 64-bit. + */ +#define LO_HALF_MASK ((1 << HALF_WORD_BITS) - 1) + +/** Get the lo half of a word. */ +#define LO_HALF(x) ((x) & LO_HALF_MASK) + +/** Get the hi half of a word. */ +#define HI_HALF(x) ((x) >> HALF_WORD_BITS) + +/** Do a full hash on both inputs, using DES-style non-linear scrambling. + * Both inputs are replaced with the results of the hash. + * + * @param pleft input/output word + * @param pright input/output word + */ +void pseudo_des(unsigned long *pleft, unsigned long *pright){ + // Bit-rich mixing constant. + static const unsigned long a_mixer[] = { + 0xbaa96887L, 0x1e17d32cL, 0x03bcdc3cL, 0x0f33d1b2L, }; + + // Bit-rich mixing constant. + static const unsigned long b_mixer[] = { + 0x4b0f3b58L, 0xe874f0c3L, 0x6955c5a6L, 0x55a7ca46L, }; + + // Number of iterations - must be 2 or 4. + static const int ncycle = 4; + //static const int ncycle = 2; + + unsigned long left = *pleft, right = *pright; + unsigned long v, v_hi, v_lo; + int i; + + for(i=0; ibuckets + (hashcode % table->buckets_n); +} + +/** Initialize a hash table. + * Can be safely called more than once. + * + * @param table to initialize + */ +void HashTable_init(HashTable *table){ + int i; + + if(!table->init_done){ + table->init_done = 1; + table->next_id = 0; + for(i=0; ibuckets_n; i++){ + HTBucket *bucket = get_bucket(table, i); + bucket->head = 0; + bucket->count = 0; + } + table->entry_count = 0; + } +} + +/** Allocate a new hashtable. + * If the number of buckets is not positive the default is used. + * The number of buckets should usually be prime. + * + * @param buckets_n number of buckets + * @return new hashtable or null + */ +HashTable *HashTable_new(int buckets_n){ + HashTable *z = ALLOCATE(HashTable); + if(!z) goto exit; + if(buckets_n <= 0){ + buckets_n = HT_BUCKETS_N; + } + z->buckets = (HTBucket*)allocate(buckets_n * sizeof(HTBucket)); + if(!z->buckets){ + deallocate(z); + z = 0; + goto exit; + } + z->buckets_n = buckets_n; + HashTable_init(z); + exit: + return z; +} + +/** Free a hashtable. + * Any entries are removed and freed. + * + * @param h hashtable (ignored if null) + */ +void HashTable_free(HashTable *h){ + if(h){ + HashTable_clear(h); + deallocate(h->buckets); + deallocate(h); + } +} + +/** Push an entry on the list in the bucket for a given hashcode. + * + * @param table to add entry to + * @param hashcode for the entry + * @param entry to add + */ +static inline void push_on_bucket(HashTable *table, Hashcode hashcode, + HTEntry *entry){ + HTBucket *bucket; + HTEntry *old_head; + + bucket = get_bucket(table, hashcode); + old_head = bucket->head; + bucket->count++; + bucket->head = entry; + entry->next = old_head; +} + +/** Change the number of buckets in a hashtable. + * No-op if the number of buckets is not positive. + * Existing entries are reallocated to buckets based on their hashcodes. + * The table is unmodified if the number of buckets cannot be changed. + * + * @param table hashtable + * @param buckets_n new number of buckets + * @return 0 on success, error code otherwise + */ +int HashTable_set_buckets_n(HashTable *table, int buckets_n){ + int err = 0; + HTBucket *old_buckets = table->buckets; + int old_buckets_n = table->buckets_n; + int i; + + if(buckets_n <= 0){ + err = -EINVAL; + goto exit; + } + table->buckets = (HTBucket*)allocate(buckets_n * sizeof(HTBucket)); + if(!table->buckets){ + err = -ENOMEM; + table->buckets = old_buckets; + goto exit; + } + table->buckets_n = buckets_n; + for(i=0; ihead; entry; entry = next){ + next = entry->next; + push_on_bucket(table, entry->hashcode, entry); + } + } + deallocate(old_buckets); + exit: + return err; +} + +/** Adjust the number of buckets so the table is neither too full nor too empty. + * The table is unmodified if adjusting fails. + * + * @param table hash table + * @param buckets_min minimum number of buckets (use default if 0 or negative) + * @return 0 on success, error code otherwise + */ +int HashTable_adjust(HashTable *table, int buckets_min){ + int buckets_n = 0; + int err = 0; + if(buckets_min <= 0) buckets_min = HT_BUCKETS_N; + if(table->entry_count >= table->buckets_n){ + // The table is dense - expand it. + buckets_n = 2 * table->buckets_n; + } else if((table->buckets_n > buckets_min) && + (4 * table->entry_count < table->buckets_n)){ + // The table is more than minimum size and sparse - shrink it. + buckets_n = 2 * table->entry_count; + if(buckets_n < buckets_min) buckets_n = buckets_min; + } + if(buckets_n){ + err = HashTable_set_buckets_n(table, buckets_n); + } + return err; +} + +/** Allocate a new entry for a given value. + * + * @param value to put in the entry + * @return entry, or 0 on failure + */ +HTEntry * HTEntry_new(Hashcode hashcode, void *key, void *value){ + HTEntry *z = ALLOCATE(HTEntry); + if(z){ + z->hashcode = hashcode; + z->key = key; + z->value = value; + } + return z; +} + +/** Free an entry. + * + * @param z entry to free + */ +inline void HTEntry_free(HTEntry *z){ + if(z){ + deallocate(z); + } +} + +/** Free an entry in a hashtable. + * The table's entry_free_fn is used is defined, otherwise + * the HTEntry itself is freed. + * + * @param table hashtable + * @param entry to free + */ +inline void HashTable_free_entry(HashTable *table, HTEntry *entry){ + if(!entry)return; + if(table && table->entry_free_fn){ + table->entry_free_fn(table, entry); + } else { + HTEntry_free(entry); + } +} + +/** Get the first entry satisfying a test from the bucket for the + * given hashcode. + * + * @param table to look in + * @param hashcode indicates the bucket + * @param test_fn test to apply to elements + * @param arg first argument to calls to test_fn + * @return entry found, or 0 + */ +inline HTEntry * HashTable_find_entry(HashTable *table, Hashcode hashcode, + TableTestFn *test_fn, TableArg arg){ + HTBucket *bucket; + HTEntry *entry = 0; + HTEntry *next; + + bucket = get_bucket(table, hashcode); + for(entry = bucket->head; entry; entry = next){ + next = entry->next; + if(test_fn(arg, table, entry)){ + break; + } + } + return entry; +} + +/** Test hashtable keys for equality. + * Uses the table's key_equal_fn if defined, otherwise pointer equality. + * + * @param key1 key to compare + * @param key2 key to compare + * @return 1 if equal, 0 otherwise + */ +inline int HashTable_key_equal(HashTable *table, void *key1, void *key2){ + return (table->key_equal_fn ? table->key_equal_fn(key1, key2) : key1==key2); +} + +/** Compute the hashcode of a hashtable key. + * The table's key_hash_fn is used if defined, otherwise the address of + * the key is hashed. + * + * @param table hashtable + * @param key to hash + * @return hashcode + */ +inline Hashcode HashTable_key_hash(HashTable *table, void *key){ + return (table->key_hash_fn ? table->key_hash_fn(key) : hash_ul((unsigned long)key)); +} + +/** Test if an entry has a given key. + * + * @param arg containing key to test for + * @param table the entry is in + * @param entry to test + * @return 1 if the entry has the key, 0 otherwise + */ +static inline int has_key(TableArg arg, HashTable *table, HTEntry *entry){ + return HashTable_key_equal(table, arg.ptr, entry->key); +} + +/** Get an entry with a given key. + * + * @param table to search + * @param key to look for + * @return entry if found, null otherwise + */ +#if 0 +inline HTEntry * HashTable_get_entry(HashTable *table, void *key){ + TableArg arg = { ptr: key }; + return HashTable_find_entry(table, HashTable_key_hash(table, key), has_key, arg); +} +#else +inline HTEntry * HashTable_get_entry(HashTable *table, void *key){ + Hashcode hashcode; + HTBucket *bucket; + HTEntry *entry = 0; + HTEntry *next; + + hashcode = HashTable_key_hash(table, key); + bucket = get_bucket(table, hashcode); + for(entry = bucket->head; entry; entry = next){ + next = entry->next; + if(HashTable_key_equal(table, key, entry->key)){ + break; + } + } + return entry; +} +#endif + +/** Get the value of an entry with a given key. + * + * @param table to search + * @param key to look for + * @return value if an entry was found, null otherwise + */ +inline void * HashTable_get(HashTable *table, void *key){ + HTEntry *entry = HashTable_get_entry(table, key); + return (entry ? entry->value : 0); +} + +/** Print the buckets in a table. + * + * @param table to print + */ +void show_buckets(HashTable *table, IOStream *io){ + int i,j ; + IOStream_print(io, "entry_count=%d buckets_n=%d\n", table->entry_count, table->buckets_n); + for(i=0; ibuckets_n; i++){ + if(0 || table->buckets[i].count>0){ + IOStream_print(io, "bucket %3d %3d %10p ", i, + table->buckets[i].count, + table->buckets[i].head); + for(j = table->buckets[i].count; j>0; j--){ + IOStream_print(io, "+"); + } + IOStream_print(io, "\n"); + } + } + HashTable_print(table, io); +} + +/** Print an entry in a table. + * + * @param entry to print + * @param arg a pointer to an IOStream to print to + * @return 0 + */ +static int print_entry(TableArg arg, HashTable *table, HTEntry *entry){ + IOStream *io = (IOStream*)arg.ptr; + IOStream_print(io, " b=%4lx h=%08lx i=%08lx |-> e=%8p k=%8p v=%8p\n", + entry->hashcode % table->buckets_n, + entry->hashcode, + entry->index, + entry, entry->key, entry->value); + return 0; +} + +/** Print a hash table. + * + * @param table to print + */ +void HashTable_print(HashTable *table, IOStream *io){ + IOStream_print(io, "{\n"); + HashTable_map(table, print_entry, (TableArg){ ptr: io }); + IOStream_print(io, "}\n"); +} +/*==========================================================================*/ + +/** Get the next entry id to use for a table. + * + * @param table hash table + * @return non-zero entry id + */ +static inline unsigned long get_next_id(HashTable *table){ + unsigned long id; + + if(table->next_id == 0){ + table->next_id = 1; + } + id = table->next_id++; + return id; +} + +/** Add an entry to the bucket for the + * given hashcode. + * + * @param table to insert in + * @param hashcode indicates the bucket + * @param key to add an entry for + * @param value to add an entry for + * @return entry on success, 0 on failure + */ +inline HTEntry * HashTable_add_entry(HashTable *table, Hashcode hashcode, void *key, void *value){ + HTEntry *entry = HTEntry_new(hashcode, key, value); + if(entry){ + entry->index = get_next_id(table); + push_on_bucket(table, hashcode, entry); + table->entry_count++; + } + return entry; +} + +/** Move the front entry for a bucket to the correct point in the bucket order as + * defined by the order function. If this is called every time a new entry is added + * the bucket will be maintained in sorted order. + * + * @param table to modify + * @param hashcode indicates the bucket + * @param order entry comparison function + * @return 0 if an entry was moved, 1 if not + */ +int HashTable_order_bucket(HashTable *table, Hashcode hashcode, TableOrderFn *order){ + HTEntry *new_entry = NULL, *prev = NULL, *entry = NULL; + HTBucket *bucket; + int err = 1; + + bucket = get_bucket(table, hashcode); + new_entry = bucket->head; + if(!new_entry || !new_entry->next) goto exit; + for(entry = new_entry->next; entry; prev = entry, entry = entry->next){ + if(order(new_entry, entry) <= 0) break; + } + if(prev){ + err = 0; + bucket->head = new_entry->next; + new_entry->next = entry; + prev->next = new_entry; + } + exit: + return err; +} + +/** Add an entry to a hashtable. + * The entry is added to the bucket for its key's hashcode. + * + * @param table to insert in + * @param key to add an entry for + * @param value to add an entry for + * @return entry on success, 0 on failure + */ +inline HTEntry * HashTable_add(HashTable *table, void *key, void *value){ + return HashTable_add_entry(table, HashTable_key_hash(table, key), key, value); +} + + +/** Remove entries satisfying a test from the bucket for the + * given hashcode. + * + * @param table to remove from + * @param hashcode indicates the bucket + * @param test_fn test to apply to elements + * @param arg first argument to calls to test_fn + * @return number of entries removed + */ +inline int HashTable_remove_entry(HashTable *table, Hashcode hashcode, + TableTestFn *test_fn, TableArg arg){ + HTBucket *bucket; + HTEntry *entry, *prev = 0, *next; + int removed_count = 0; + + bucket = get_bucket(table, hashcode); + for(entry = bucket->head; entry; entry = next){ + next = entry->next; + if(test_fn(arg, table, entry)){ + if(prev){ + prev->next = next; + } else { + bucket->head = next; + } + bucket->count--; + table->entry_count--; + removed_count++; + HashTable_free_entry(table, entry); + entry = 0; + } + prev = entry; + } + return removed_count; +} + +/** Remove entries with a given key. + * + * @param table to remove from + * @param key of entries to remove + * @return number of entries removed + */ +inline int HashTable_remove(HashTable *table, void *key){ +#if 1 + Hashcode hashcode; + HTBucket *bucket; + HTEntry *entry, *prev = 0, *next; + int removed_count = 0; + + hashcode = HashTable_key_hash(table, key); + bucket = get_bucket(table, hashcode); + for(entry = bucket->head; entry; entry = next){ + next = entry->next; + if(HashTable_key_equal(table, key, entry->key)){ + if(prev){ + prev->next = next; + } else { + bucket->head = next; + } + bucket->count--; + table->entry_count--; + removed_count++; + HashTable_free_entry(table, entry); + entry = 0; + } + prev = entry; + } + return removed_count; +#else + return HashTable_remove_entry(table, HashTable_key_hash(table, key), + has_key, (TableArg){ ptr: key}); +#endif +} + +/** Remove (and free) all the entries in a bucket. + * + * @param bucket to clear + */ +static inline void bucket_clear(HashTable *table, HTBucket *bucket){ + HTEntry *entry, *next; + + for(entry = bucket->head; entry; entry = next){ + next = entry->next; + HashTable_free_entry(table, entry); + } + bucket->head = 0; + table->entry_count -= bucket->count; + bucket->count = 0; +} + +/** Remove (and free) all the entries in a table. + * + * @param table to clear + */ +void HashTable_clear(HashTable *table){ + int i, n = table->buckets_n; + + for(i=0; ibuckets + i); + } +} diff --git a/tools/xfrd/hash_table.h b/tools/xfrd/hash_table.h new file mode 100644 index 0000000000..6608b49cda --- /dev/null +++ b/tools/xfrd/hash_table.h @@ -0,0 +1,294 @@ +/* + * Copyright (C) 2001 - 2004 Mike Wray + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _XUTIL_HASH_TABLE_H_ +#define _XUTIL_HASH_TABLE_H_ + +#include "iostream.h" + +typedef unsigned long Hashcode; + +/** Type used to pass parameters to table functions. */ +typedef union TableArg { + unsigned long ul; + void *ptr; +} TableArg; + +/** An entry in a bucket list. */ +typedef struct HTEntry { + /** Hashcode of the entry's key. */ + Hashcode hashcode; + /** Identifier for this entry in the table. */ + int index; + /** The key for this entry. */ + void *key; + /** The value in this entry. */ + void *value; + /** The next entry in the list. */ + struct HTEntry *next; +} HTEntry; + +/** A bucket in a rule table. */ +typedef struct HTBucket { + /** Number of entries in the bucket. */ + int count; + /** First entry in the bucket (may be null). */ + HTEntry *head; +} HTBucket; + +/** Default number of buckets in a hash table. + * You want enough buckets so the lists in the buckets will typically be short. + * It's a good idea if this is prime, since that will help to spread hashcodes + * around the table. + */ +//#define HT_BUCKETS_N 1 +//#define HT_BUCKETS_N 3 +//#define HT_BUCKETS_N 7 +//#define HT_BUCKETS_N 17 +//#define HT_BUCKETS_N 97 +//#define HT_BUCKETS_N 211 +//#define HT_BUCKETS_N 401 +#define HT_BUCKETS_N 1021 + +typedef struct HashTable HashTable; + +/** Type for a function used to select table entries. */ +typedef int TableTestFn(TableArg arg, HashTable *table, HTEntry *entry); + +/** Type for a function to map over table entries. */ +typedef int TableMapFn(TableArg arg, HashTable *table, HTEntry *entry); + +/** Type for a function to free table entries. */ +typedef void TableFreeFn(HashTable *table, HTEntry *entry); + +/** Type for a function to hash table keys. */ +typedef Hashcode TableHashFn(void *key); + +/** Type for a function to test table keys for equality. */ +typedef int TableEqualFn(void *key1, void *key2); + +/** Type for a function to order table entries. */ +typedef int TableOrderFn(HTEntry *e1, HTEntry *e2); + +/** General hash table. + * A hash table with a list in each bucket. + * Functions can be supplied for freeing entries, hashing keys, and comparing keys. + * These all default to 0, when default behaviour treating keys as integers is used. + */ +struct HashTable { + /** Flag indicating whether the table has been initialised. */ + int init_done; + /** Next value for the id field in inserted rules. */ + unsigned long next_id; + /** Number of buckets in the bucket array. */ + int buckets_n; + /** Array of buckets, each with its own list. */ + HTBucket *buckets; + /** Number of entries in the table. */ + int entry_count; + /** Function to free keys and values in entries. */ + TableFreeFn *entry_free_fn; + /** Function to hash keys. */ + TableHashFn *key_hash_fn; + /** Function to compare keys for equality. */ + TableEqualFn *key_equal_fn; + /** Place for the user of the table to hang extra data. */ + void *user_data; +}; + +extern HashTable *HashTable_new(int bucket_n); +extern void HashTable_free(HashTable *table); +extern HTEntry * HTEntry_new(Hashcode hashcode, void *key, void *value); +extern void HTEntry_free(HTEntry *entry); +extern int HashTable_set_bucket_n(HashTable *table, int bucket_n); +extern void HashTable_clear(HashTable *table); +extern HTEntry * HashTable_add_entry(HashTable *table, Hashcode hashcode, void *key, void *value); +extern HTEntry * HashTable_get_entry(HashTable *table, void *key); +extern HTEntry * HashTable_add(HashTable *table, void *key, void *value); +extern void * HashTable_get(HashTable *table, void *key); +extern int HashTable_remove(HashTable *table, void *key); +extern HTEntry * HashTable_find_entry(HashTable *table, Hashcode hashcode, + TableTestFn *test_fn, TableArg arg); +extern int HashTable_remove_entry(HashTable *table, Hashcode hashcode, + TableTestFn *test_fn, TableArg arg); +//extern int HashTable_map(HashTable *table, TableMapFn *map_fn, TableArg arg); +extern void HashTable_print(HashTable *table, IOStream *out); +extern int HashTable_set_buckets_n(HashTable *table, int buckets_n); +extern int HashTable_adjust(HashTable *table, int buckets_min); +extern void pseudo_des(unsigned long *pleft, unsigned long *pright); +extern Hashcode hash_string(char *s); + +extern int HashTable_order_bucket(HashTable *table, Hashcode hashcode, TableOrderFn *order); + +/** Control whether to use hashing based on DES or simple + * hashing. DES hashing is `more random' but much more expensive. + */ +#define HASH_PSEUDO_DES 0 + +/** Hash a long using a quick and dirty linear congruential random number generator. + * See `Numerical Recipes in C', Chapter 7, "An Even Quicker Generator". + * + * @param a value to hash + * @return hashed input + */ +static inline unsigned long lcrng_hash(unsigned long a){ + return (1664525L * a + 1013904223L); +} + +/** Hash an unsigned long. + * + * @param a input to hash + * @return hashcode + */ +static inline Hashcode hash_ul(unsigned long a){ +#if HASH_PSEUDO_DES + unsigned long left = a; + unsigned long right = 0L; + pseudo_des(&left, &right); + return right; +#else + a = lcrng_hash(a); + a = lcrng_hash(a); + return a; +#endif +} + +/** Hash two unsigned longs together. + * + * @param a input to hash + * @param b input to hash + * @return hashcode + */ +static inline Hashcode hash_2ul(unsigned long a, unsigned long b){ +#if HASH_PSEUDO_DES + unsigned long left = a; + unsigned long right = b; + pseudo_des(&left, &right); + return right; +#else + a = lcrng_hash(a); + a ^= b; + a = lcrng_hash(a); + return a; +#endif +} + +/** Hash a hashcode and an unsigned long together. + * + * @param a input hashcode + * @param b input to hash + * @return hashcode + */ +static inline Hashcode hash_hul(Hashcode a, unsigned long b){ +#if HASH_PSEUDO_DES + unsigned long left = a; + unsigned long right = b; + pseudo_des(&left, &right); + return right; +#else + a ^= b; + a = lcrng_hash(a); + return a; +#endif +} + +/** Macro to declare variables for HashTable_for_each() to use. + * + * @param entry variable that is set to entries in the table + */ +#define HashTable_for_decl(entry) \ + HashTable *_var_table; \ + HTBucket *_var_bucket; \ + HTBucket *_var_end; \ + HTEntry *_var_next; \ + HTEntry *entry + +/** Macro to iterate over the entries in a hashtable. + * Must be in a scope where HashTable_for_decl() has been used to declare + * variables for it to use. + * The variable 'entry' is iterated over entries in the table. + * The code produced is syntactically a loop, so it must be followed by + * a loop body, typically some statements in braces: + * HashTable_for_each(entry, table){ ...loop body... } + * + * HashTable_for_each() and HashTable_for_decl() cannot be used for nested + * loops as variables will clash. + * + * @note The simplest way to code a direct loop over the entries in a hashtable + * is to use a loop over the buckets, with a nested loop over the entries + * in a bucket. Using this approach in a macro means the macro contains + * an opening brace, and calls to it must be followed by 2 braces! + * To avoid this the code has been restructured so that it is a for loop. + * So that statements could be used in the test expression of the for loop, + * we have used the gcc statement expression extension ({ ... }). + * + * @param entry variable to iterate over the entries + * @param table to iterate over (non-null) + */ +#define HashTable_for_each(entry, table) \ + _var_table = table; \ + _var_bucket = _var_table->buckets; \ + _var_end = _var_bucket + _var_table->buckets_n; \ + for(entry=0, _var_next=0; \ + ({ if(_var_next){ \ + entry = _var_next; \ + _var_next = entry->next; \ + } else { \ + while(_var_bucket < _var_end){ \ + entry = _var_bucket->head; \ + _var_bucket++; \ + if(entry){ \ + _var_next = entry->next; \ + break; \ + } \ + } \ + }; \ + entry; }); \ + entry = _var_next ) + +/** Map a function over the entries in a table. + * Mapping stops when the function returns a non-zero value. + * Uses the gcc statement expression extension ({ ... }). + * + * @param table to map over + * @param fn function to apply to entries + * @param arg first argument to call the function with + * @return 0 if fn always returned 0, first non-zero value otherwise + */ +#define HashTable_map(table, fn, arg) \ + ({ HashTable_for_decl(_var_entry); \ + TableArg _var_arg = arg; \ + int _var_value = 0; \ + HashTable_for_each(_var_entry, table){ \ + if((_var_value = fn(_var_arg, _var_table, _var_entry))) break; \ + } \ + _var_value; }) + +/** Cast x to the type for a key or value in a hash table. + * This avoids compiler warnings when using short integers + * as keys or values (especially on 64-bit platforms). + */ +#define HKEY(x) ((void*)(unsigned long)(x)) + +/** Cast x from the type for a key or value in a hash table. + * to an unsigned long. This avoids compiler warnings when using + * short integers as keys or values (especially on 64-bit platforms). + */ +#define HVAL(x) ((unsigned long)(x)) + +#endif /* !_XUTIL_HASH_TABLE_H_ */ diff --git a/tools/xfrd/lexis.c b/tools/xfrd/lexis.c new file mode 100644 index 0000000000..26d2ec4d5b --- /dev/null +++ b/tools/xfrd/lexis.c @@ -0,0 +1,93 @@ +/* + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. This library is + * distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/** @file + * Lexical analysis. + */ + +#include "sys_string.h" +#include "lexis.h" +#include + +/** Check if a value lies in a (closed) range. + * + * @param x value to test + * @param lo low end of the range + * @param hi high end of the range + * @return 1 if x is in the interval [lo, hi], 0 otherwise + */ +inline static int in_range(int x, int lo, int hi){ + return (lo <= x) && (x <= hi); +} + +/** Determine if a string is an (unsigned) decimal number. + * + * @param s pointer to characters to test + * @param n length of string + * @return 1 if s is a decimal number, 0 otherwise. + */ +int is_decimal_number(const char *s, int n){ + int i; + if(n <= 0)return 0; + for(i = 0; i < n; i++){ + if(!in_decimal_digit_class(s[i])) return 0; + } + return 1; +} + +/** Determine if a string is a hex number. + * Hex numbers are 0, or start with 0x or 0X followed + * by a non-zero number of hex digits (0-9,a-f,A-F). + * + * @param s pointer to characters to test + * @param n length of string + * @return 1 if s is a hex number, 0 otherwise. + */ +int is_hex_number(const char *s, int n){ + int i; + if(n <= 0) return 0; + if(n == 1){ + return s[0]=='0'; + } + if(n <= 3) return 0; + if(s[0] != '0' || (s[1] != 'x' && s[1] != 'X')) return 0; + for(i = 2; i < n; i++){ + if(!in_hex_digit_class(s[i])) return 0; + } + return 1; +} + +/** Test if a string matches a keyword. + * The comparison is case-insensitive. + * The comparison fails if either argument is null. + * + * @param s string + * @param k keyword + * @return 1 if they match, 0 otherwise + */ +int is_keyword(const char *s, const char *k){ + return s && k && !strcasecmp(s, k); +} + +/** Test if a string matches a character. + * + * @param s string + * @param c character (non-null) + * @return 1 if s contains exactly c, 0 otherwise + */ +int is_keychar(const char *s, char c){ + return c && (s[0] == c) && !s[1]; +} diff --git a/tools/xfrd/lexis.h b/tools/xfrd/lexis.h new file mode 100644 index 0000000000..796a8a415a --- /dev/null +++ b/tools/xfrd/lexis.h @@ -0,0 +1,127 @@ +/* + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. This library is + * distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _XUTIL_LEXIS_H_ +#define _XUTIL_LEXIS_H_ + +#include "sys_string.h" + +#ifdef __KERNEL__ +# include +#else +# include +#endif + +/** @file + * Lexical analysis. + */ + +/** Class of characters treated as space. */ +#define space_class ((char []){ '\n', '\r', '\t', ' ', '\f' , 0 }) + +/** Class of separator characters. */ +#define sep_class "{}()<>[]@!;" + +#define comment_class "#" + +/** Determine if a character is in a given class. + * + * @param c character to test + * @param s null-terminated string of characters in the class + * @return 1 if c is in the class, 0 otherwise. + */ +static inline int in_class(int c, const char *s){ + return s && (strchr(s, c) != 0); +} + +/** Determine if a character is in the space class. + * + * @param c character to test + * @return 1 if c is in the class, 0 otherwise. + */ +static inline int in_space_class(int c){ + return in_class(c, space_class); +} + +static inline int in_comment_class(int c){ + return in_class(c, comment_class); +} + +/** Determine if a character is in the separator class. + * Separator characters terminate tokens, and do not need space + * to separate them. + * + * @param c character to test + * @return 1 if c is in the class, 0 otherwise. + */ +static inline int in_sep_class(int c){ + return in_class(c, sep_class); +} + +/** Determine if a character is in the alpha class. + * + * @param c character to test + * @return 1 if c is in the class, 0 otherwise. + */ +static inline int in_alpha_class(int c){ + return isalpha(c); +} + +/** Determine if a character is in the octal digit class. + * + * @param c character to test + * @return 1 if c is in the class, 0 otherwise. + */ +static inline int in_octal_digit_class(int c){ + return '0' <= c && c <= '7'; +} + +/** Determine if a character is in the decimal digit class. + * + * @param c character to test + * @return 1 if c is in the class, 0 otherwise. + */ +static inline int in_decimal_digit_class(int c){ + return isdigit(c); +} + +/** Determine if a character is in the hex digit class. + * + * @param c character to test + * @return 1 if c is in the class, 0 otherwise. + */ +static inline int in_hex_digit_class(int c){ + return isdigit(c) || in_class(c, "abcdefABCDEF"); +} + + +static inline int in_string_quote_class(int c){ + return in_class(c, "'\""); +} + +static inline int in_printable_class(int c){ + return ('A' <= c && c <= 'Z') + || ('a' <= c && c <= 'z') + || ('0' <= c && c <= '9') + || in_class(c, "!$%&*+,-./:;<=>?@^_`{|}~"); +} + +extern int is_decimal_number(const char *s, int n); +extern int is_hex_number(const char *s, int n); +extern int is_keyword(const char *s, const char *k); +extern int is_keychar(const char *s, char c); + +#endif /* !_XUTIL_LEXIS_H_ */ diff --git a/tools/xfrd/lzi_stream.c b/tools/xfrd/lzi_stream.c new file mode 100644 index 0000000000..5fbec775c4 --- /dev/null +++ b/tools/xfrd/lzi_stream.c @@ -0,0 +1,533 @@ +/* + * Copyright (C) 2003 Hewlett-Packard Company. + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/** @file + * An IOStream implementation using LZI to provide compression and decompression. + * This is designed to provide compression without output latency. + * Flushing an LZI stream flushes all pending data to the underlying stream. + * This is essential for stream-based (e.g. networked) applications. + * + * A compressed data stream is a sequence of blocks. + * Each block is the block size followed by the compressed data. + * The last block has size zero. + * Sizes are 4-byte unsigned in network order. + * + * This format allows compressed data to be read from a stream without reading + * past the logical end of compressed data. + * + * @author Mike Wray + */ +#ifndef __KERNEL__ + +#include +#include +#include +#include + +#include "zlib.h" + +#include "allocate.h" +#include "lzi_stream.h" +#include "file_stream.h" +#include "marshal.h" + +#define dprintf(fmt, args...) fprintf(stdout, "[DEBUG] LZI>%s" fmt, __FUNCTION__, ##args) +#define wprintf(fmt, args...) fprintf(stderr, "[WARN] LZI>%s" fmt, __FUNCTION__, ##args) +#define iprintf(fmt, args...) fprintf(stdout, "[INFO] LZI>%s" fmt, __FUNCTION__, ##args) +#define eprintf(fmt, args...) fprintf(stderr, "[ERROR] LZI>%s" fmt, __FUNCTION__, ##args) + +static int lzi_read(IOStream *s, void *buf, size_t n); +static int lzi_write(IOStream *s, const void *buf, size_t n); +static int lzi_error(IOStream *s); +static int lzi_close(IOStream *s); +static void lzi_free(IOStream *s); +static int lzi_flush(IOStream *s); + +enum { + LZI_WRITE = 1, + LZI_READ = 2, +}; + +/** Methods used by a gzFile* IOStream. */ +static const IOMethods lzi_methods = { + read: lzi_read, + write: lzi_write, + error: lzi_error, + close: lzi_close, + free: lzi_free, + flush: lzi_flush, +}; + +#define BUFFER_SIZE (512 * 1024) + +typedef struct LZIState { + z_stream zstream; + void *inbuf; + uint32_t inbuf_size; + void *outbuf; + uint32_t outbuf_size; + /** Underlying stream for I/O. */ + IOStream *io; + /** Flags. */ + int flags; + /** Error indicator. */ + int error; + int eof; + int plain_bytes; + int comp_bytes; + int zstream_initialized; + int flushed; +} LZIState; + +static inline int LZIState_writeable(LZIState *s){ + return (s->flags & LZI_WRITE) != 0; +} + +static inline int LZIState_readable(LZIState *s){ + return (s->flags & LZI_READ) != 0; +} + +void LZIState_free(LZIState *z){ + if(!z) return; + if(z->zstream_initialized){ + if(LZIState_writeable(z)){ + deflateEnd(&z->zstream); + } else if(LZIState_readable(z)){ + inflateEnd(&z->zstream); + } + } + deallocate(z->inbuf); + deallocate(z->outbuf); + deallocate(z); +} + +static int mode_flags(const char *mode, int *flags){ + int err = 0; + int r=0, w=0; + if(!mode){ + err = -EINVAL; + goto exit; + } + for(; *mode; mode++){ + if(*mode == 'w') w = 1; + if(*mode == 'r') r = 1; + } + if(r + w != 1){ + err = -EINVAL; + goto exit; + } + if(r) *flags |= LZI_READ; + if(w) *flags |= LZI_WRITE; + exit: + return err; +} + +/** Get the stream state. + * + * @param s lzi stream + * @return stream state. + */ +static inline LZIState * lzi_state(IOStream *io){ + return (LZIState*)io->data; +} + +IOStream *lzi_stream_io(IOStream *io){ + LZIState *s = lzi_state(io); + return s->io; +} + +static inline void set_error(LZIState *s, int err){ + if(err < 0 && !s->error){ + s->error = err; + } +} + +static int zerror(LZIState *s, int err){ + if(err){ + //dprintf("> err=%d\n", err); + if(err < 0) set_error(s, -EIO); + } + return s->error; +} + +int lzi_stream_plain_bytes(IOStream *io){ + LZIState *s = lzi_state(io); + return s->plain_bytes; +} + +int lzi_stream_comp_bytes(IOStream *io){ + LZIState *s = lzi_state(io); + return s->comp_bytes; +} + +float lzi_stream_ratio(IOStream *io){ + LZIState *s = lzi_state(io); + float ratio = 0.0; + if(s->comp_bytes){ + ratio = ((float) s->comp_bytes)/((float) s->plain_bytes); + } + return ratio; +} + +static int alloc(void **p, int n){ + *p = allocate(n); + return (p ? 0 : -ENOMEM); +} + +LZIState * LZIState_new(IOStream *io, int flags){ + int err = -ENOMEM; + int zlevel = Z_BEST_SPEED; // Level 1 compression - fastest. + int zstrategy = Z_DEFAULT_STRATEGY; + int zwindow = MAX_WBITS; + int zmemory = 8; + LZIState *z = ALLOCATE(LZIState); + + //dprintf(">\n"); + if(!z) goto exit; + z->io = io; + z->flags = flags; + + if(LZIState_writeable(z)){ + z->outbuf_size = BUFFER_SIZE; + /* windowBits is passed < 0 to suppress zlib header */ + err = deflateInit2(&z->zstream, zlevel, Z_DEFLATED, -zwindow, zmemory, zstrategy); + if (err != Z_OK) goto exit; + z->zstream_initialized = 1; + err = alloc(&z->outbuf, z->outbuf_size); + if(err) goto exit; + z->zstream.next_out = z->outbuf; + z->zstream.avail_out = z->outbuf_size; + } else { + z->inbuf_size = BUFFER_SIZE; + err = alloc(&z->inbuf, z->inbuf_size); + if(err) goto exit; + ///z->zstream.next_in = z->inbuf; + + /* windowBits is passed < 0 to tell that there is no zlib header. + * Note that in this case inflate *requires* an extra "dummy" byte + * after the compressed stream in order to complete decompression and + * return Z_STREAM_END. Here the gzip CRC32 ensures that 4 bytes are + * present after the compressed stream. + */ + err = inflateInit2(&z->zstream, -zwindow); + if(err != Z_OK) goto exit; + z->zstream_initialized = 1; + } + + exit: + if(err){ + LZIState_free(z); + z = NULL; + } + //dprintf("< z=%p\n", z); + return z; +} + +int read_block(LZIState *s){ + int err = 0, k = 0; + //dprintf(">\n"); + if(s->eof) goto exit; + err = unmarshal_uint32(s->io, &k); + if(err) goto exit; + if(k > s->inbuf_size){ + err = -EINVAL; + goto exit; + } + if(k){ + err = unmarshal_bytes(s->io, s->inbuf, k); + if(err) goto exit; + } else { + s->eof = 1; + } + s->zstream.avail_in = k; + s->zstream.next_in = s->inbuf; + s->comp_bytes += 4; + s->comp_bytes += k; + exit: + //dprintf("< err=%d\n", err); + return err; +} + +int write_block(LZIState *s){ + int err = 0; + int k = ((char*)s->zstream.next_out) - ((char*)s->outbuf); + //int k2 = s->outbuf_size - s->zstream.avail_out; + //dprintf("> k=%d k2=%d\n", k, k2); + if(!k) goto exit; + err = marshal_uint32(s->io, k); + if(err) goto exit; + err = marshal_bytes(s->io, s->outbuf, k); + if(err) goto exit; + s->zstream.next_out = s->outbuf; + s->zstream.avail_out = s->outbuf_size; + s->comp_bytes += 4; + s->comp_bytes += k; + exit: + //dprintf("< err=%d\n", err); + return err; +} + +int write_terminator(LZIState *s){ + int err = 0; + char c = 0; + err = marshal_uint32(s->io, 1); + if(err) goto exit; + err = marshal_bytes(s->io, &c, 1); + if(err) goto exit; + err = marshal_uint32(s->io, 0); + if(err) goto exit; + s->comp_bytes += 9; + exit: + return err; +} + +/** Write to the underlying stream using fwrite(); + * + * @param io destination + * @param buf data + * @param n number of bytes to write + * @return number of bytes written + */ +static int lzi_write(IOStream *io, const void *buf, size_t n){ + int err = 0; + LZIState *s = lzi_state(io); + + //dprintf("> buf=%p n=%d\n", buf, n); + if(!LZIState_writeable(s)){ + err = -EINVAL; + goto exit; + } + s->flushed = 0; + s->zstream.next_in = (void*)buf; + s->zstream.avail_in = n; + while(s->zstream.avail_in){ + if(s->zstream.avail_out == 0){ + err = write_block(s); + if(err) goto exit; + } + //dprintf("> 1 deflate avail_in=%d avail_out=%d\n", s->zstream.avail_in, s->zstream.avail_out); + //dprintf("> 1 deflate next_in=%p next_out=%p\n", s->zstream.next_in, s->zstream.next_out); + err = zerror(s, deflate(&s->zstream, Z_NO_FLUSH)); + //dprintf("> 2 deflate avail_in=%d avail_out=%d\n", s->zstream.avail_in, s->zstream.avail_out); + //dprintf("> 2 deflate next_in=%p next_out=%p\n", s->zstream.next_in, s->zstream.next_out); + if(err) goto exit; + } + err = n; + s->plain_bytes += n; + exit: + //dprintf("< err=%d\n", err); + return err; +} + + +/** Read from the underlying stream. + * + * @param io input + * @param buf where to put input + * @param n number of bytes to read + * @return number of bytes read + */ +static int lzi_read(IOStream *io, void *buf, size_t n){ + int err, zerr; + LZIState *s = lzi_state(io); + + //dprintf("> n=%d\n", n); + if(!LZIState_readable(s)){ + err = -EINVAL; + goto exit; + } + s->zstream.next_out = buf; + s->zstream.avail_out = n; + while(s->zstream.avail_out){ + if(s->zstream.avail_in == 0){ + err = read_block(s); + } + //dprintf("> 1 deflate avail_in=%d avail_out=%d\n", s->zstream.avail_in, s->zstream.avail_out); + zerr = inflate(&s->zstream, Z_NO_FLUSH); + //dprintf("> 2 deflate avail_in=%d avail_out=%d\n", s->zstream.avail_in, s->zstream.avail_out); + if(zerr == Z_STREAM_END) break; + //dprintf("> zerr=%d\n", zerr); + err = zerror(s, zerr); + if(err) goto exit; + } + err = n - s->zstream.avail_out; + s->plain_bytes += err; + exit: + set_error(s, err); + //dprintf("< err=%d\n", err); + return err; +} + +static int flush_output(LZIState *s, int mode){ + int err = 0, zerr; + int done = 0; + int avail_out_old; + + //dprintf("> avail_in=%d avail_out=%d\n", s->zstream.avail_in, s->zstream.avail_out); + if(s->flushed == 1 + mode) goto exit; + //s->zstream.avail_in = 0; /* should be zero already anyway */ + for(;;){ + // Write any available output. + if(done || s->zstream.avail_out == 0){ + err = write_block(s); + if(err) goto exit; + if(done) break; + } + //dprintf("> 1 deflate avail_in=%d avail_out=%d\n", s->zstream.avail_in, s->zstream.avail_out); + avail_out_old = s->zstream.avail_out; + zerr = deflate(&s->zstream, mode); + err = zerror(s, zerr); + //dprintf("> 2 deflate avail_in=%d avail_out=%d\n", s->zstream.avail_in, s->zstream.avail_out); + //dprintf("> deflate=%d\n", err); + //done = (s->zstream.avail_out != 0); + //done = (s->zstream.avail_in == 0) && (s->zstream.avail_out == avail_out_old); + if(0 && mode == Z_FINISH){ + done = (zerr == Z_STREAM_END); + } else { + done = (s->zstream.avail_in == 0) + //&& (s->zstream.avail_out == avail_out_old) + && (s->zstream.avail_out != 0); + } + } + s->flushed = 1 + mode; + exit: + //dprintf("< err=%d\n", err); + return err; +} + +/** Flush any pending input to the underlying stream. + * + * @param s lzi stream + * @return 0 on success, error code otherwise + */ +static int lzi_flush(IOStream *io){ + int err = 0; + LZIState *s = lzi_state(io); + //dprintf(">\n"); + if(!LZIState_writeable(s)){ + err = -EINVAL; + goto exit; + } + err = flush_output(s, Z_SYNC_FLUSH); + if(err) goto exit; + err = IOStream_flush(s->io); + exit: + set_error(s, err); + //dprintf("< err=%d\n", err); + return (err < 0 ? err : 0); +} + +/** Check if a stream has an error. + * + * @param s lzi stream + * @return code if has an error, 0 otherwise + */ +static int lzi_error(IOStream *s){ + int err = 0; + LZIState *state = lzi_state(s); + err = state->error; + if(err) goto exit; + err = IOStream_error(state->io); + exit: + return err; +} + +/** Close an lzi stream. + * + * @param s lzi stream to close + * @return result of the close + */ +static int lzi_close(IOStream *io){ + int err = 0; + LZIState *s = lzi_state(io); + if(LZIState_writeable(s)){ + err = flush_output(s, Z_FINISH); + if(err) goto exit; + err = write_terminator(s); + if(err) goto exit; + err = IOStream_flush(s->io); + } + exit: + err = IOStream_close(s->io); + set_error(s, err); + return err; +} + +/** Free an lzi stream. + * + * @param s lzi stream + */ +static void lzi_free(IOStream *s){ + LZIState *state = lzi_state(s); + IOStream_free(state->io); + LZIState_free(state); + s->data = NULL; +} + +/** Create an lzi stream for an IOStream. + * + * @param io stream to wrap + * @return new IOStream using f for i/o + */ +IOStream *lzi_stream_new(IOStream *io, const char *mode){ + int err = -ENOMEM; + int flags = 0; + IOStream *zio = NULL; + LZIState *state = NULL; + + zio = ALLOCATE(IOStream); + if(!zio) goto exit; + err = mode_flags(mode, &flags); + if(err) goto exit; + state = LZIState_new(io, flags); + if(!state) goto exit; + err = 0; + zio->data = state; + zio->methods = &lzi_methods; + exit: + if(err){ + if(state) LZIState_free(state); + if(zio) deallocate(zio); + zio = NULL; + } + return zio; +} + +/** IOStream version of fdopen(). + * + * @param fd file descriptor + * @param flags giving the mode to open in (as for fdopen()) + * @return new stream for the open file, or NULL if failed + */ +IOStream *lzi_stream_fdopen(int fd, const char *mode){ + int err = -ENOMEM; + IOStream *io = NULL, *zio = NULL; + io = file_stream_fdopen(fd, mode); + if(!io) goto exit; + zio = lzi_stream_new(io, mode); + if(!io) goto exit; + err = 0; + exit: + if(err){ + IOStream_free(io); + IOStream_free(zio); + zio = NULL; + } + return zio; +} +#endif diff --git a/tools/xfrd/lzi_stream.h b/tools/xfrd/lzi_stream.h new file mode 100644 index 0000000000..959059eccf --- /dev/null +++ b/tools/xfrd/lzi_stream.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2003 Hewlett-Packard Company. + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _XUTIL_LZI_STREAM_H_ +#define _XUTIL_LZI_STREAM_H_ + +#ifndef __KERNEL__ +#include "iostream.h" + +extern IOStream *lzi_stream_new(IOStream *io, const char *mode); +extern IOStream *lzi_stream_fopen(const char *file, const char *mode); +extern IOStream *lzi_stream_fdopen(int fd, const char *mode); +extern IOStream *lzi_stream_io(IOStream *zio); + +extern int lzi_stream_plain_bytes(IOStream *io); +extern int lzi_stream_comp_bytes(IOStream *io); +extern float lzi_stream_ratio(IOStream *io); + +#endif +#endif /* !_XUTIL_LZI_STREAM_H_ */ diff --git a/tools/xfrd/marshal.c b/tools/xfrd/marshal.c new file mode 100644 index 0000000000..21691d4412 --- /dev/null +++ b/tools/xfrd/marshal.c @@ -0,0 +1,207 @@ +#include +#include "sys_net.h" +#include "allocate.h" +#include "marshal.h" + +#define dprintf(fmt, args...) IOStream_print(iostdout, "[DEBUG] %s" fmt, __FUNCTION__, ##args) +#define wprintf(fmt, args...) IOStream_print(iostderr, "[WARN] %s" fmt, __FUNCTION__, ##args) +#define iprintf(fmt, args...) IOStream_print(iostdout, "[INFO] %s" fmt, __FUNCTION__, ##args) +#define eprintf(fmt, args...) IOStream_print(iostderr, "[ERROR] %s" fmt, __FUNCTION__, ##args) + + +#define ARRAY_SIZE(ary) (sizeof(ary)/sizeof((ary)[0])) + +/* Messages are coded as msgid followed by message fields. + * Initial message on any channel is hello - so can check version + * compatibility. + * + * char* -> uint16_t:n + * ints/uints go as suitable number of bytes (e.g. uint16_t is 2 bytes). + * optional fields go as '1' or '0' (the 0/1 is 1 byte). + * lists go as ('1' )* '0' + */ + +int marshal_flush(IOStream *io){ + int err = 0; + err = IOStream_flush(io); + return err; +} + +int marshal_bytes(IOStream *io, void *s, uint32_t s_n){ + int err = 0; + int n; + n = IOStream_write(io, s, s_n); + if(n < 0){ + err = n; + } else if (n < s_n){ + wprintf("> Wanted %d, got %d\n", s_n, n); + err = -EIO; + } + return err; +} + +int unmarshal_bytes(IOStream *io, void *s, uint32_t s_n){ + int err = 0; + int n; + //dprintf("> s_n=%d\n", s_n); + n = IOStream_read(io, s, s_n); + //dprintf("> n=%d\n", n); + if(n < 0){ + err = n; + } else if(n < s_n){ + wprintf("> Wanted %d, got %d\n", s_n, n); + err = -EIO; + } + //dprintf("< err=%d\n", err); + return err; +} + +int marshal_uint8(IOStream *io, uint8_t x){ + return marshal_bytes(io, &x, sizeof(x)); +} + +int unmarshal_uint8(IOStream *io, uint8_t *x){ + return unmarshal_bytes(io, x, sizeof(*x)); +} + +int marshal_uint16(IOStream *io, uint16_t x){ + x = htons(x); + return marshal_bytes(io, &x, sizeof(x)); +} + +int unmarshal_uint16(IOStream *io, uint16_t *x){ + int err = 0; + err = unmarshal_bytes(io, x, sizeof(*x)); + *x = ntohs(*x); + return err; +} + +int marshal_int32(IOStream *io, int32_t x){ + int err = 0; + //dprintf("> x=%d\n", x); + x = htonl(x); + err = marshal_bytes(io, &x, sizeof(x)); + //dprintf("< err=%d\n", err); + return err; +} + +int unmarshal_int32(IOStream *io, int32_t *x){ + int err = 0; + //dprintf(">\n"); + err = unmarshal_bytes(io, x, sizeof(*x)); + *x = ntohl(*x); + //dprintf("< err=%d x=%d\n", err, *x); + return err; +} + +int marshal_uint32(IOStream *io, uint32_t x){ + int err = 0; + //dprintf("> x=%u\n", x); + x = htonl(x); + err = marshal_bytes(io, &x, sizeof(x)); + //dprintf("< err=%d\n", err); + return err; +} + +int unmarshal_uint32(IOStream *io, uint32_t *x){ + int err = 0; + //dprintf(">\n"); + err = unmarshal_bytes(io, x, sizeof(*x)); + *x = ntohl(*x); + //dprintf("< err=%d x=%u\n", err, *x); + return err; +} + +int marshal_uint64(IOStream *io, uint64_t x){ + int err; + err = marshal_uint32(io, (uint32_t) ((x >> 32) & 0xffffffff)); + if(err) goto exit; + err = marshal_uint32(io, (uint32_t) ( x & 0xffffffff)); + exit: + return err; +} + +int unmarshal_uint64(IOStream *io, uint64_t *x){ + int err = 0; + uint32_t hi, lo; + err = unmarshal_uint32(io, &hi); + if(err) goto exit; + err = unmarshal_uint32(io, &lo); + *x = (((uint64_t) hi) << 32) | lo; + exit: + return err; +} + +int marshal_net16(IOStream *io, net16_t x){ + return marshal_bytes(io, &x, sizeof(x)); +} + +int unmarshal_net16(IOStream *io, net16_t *x){ + int err = 0; + err = unmarshal_bytes(io, x, sizeof(*x)); + return err; +} + +int marshal_net32(IOStream *io, net32_t x){ + return marshal_bytes(io, &x, sizeof(x)); +} + +int unmarshal_net32(IOStream *io, net32_t *x){ + int err = 0; + err = unmarshal_bytes(io, x, sizeof(*x)); + return err; +} + +int marshal_string(IOStream *io, char *s, uint32_t s_n){ + int err; + //dprintf("> s=%s\n", s); + err = marshal_uint32(io, s_n); + if(err) goto exit; + err = marshal_bytes(io, s, s_n); + exit: + //dprintf("< err=%d\n", err); + return err; +} + +int unmarshal_string(IOStream *io, char *s, uint32_t s_n){ + int err = 0, val_n = 0; + //dprintf(">\n"); + err = unmarshal_uint32(io, &val_n); + if(err) goto exit; + if(val_n >= s_n){ + err = -EINVAL; + goto exit; + } + err = unmarshal_bytes(io, s, val_n); + if(err) goto exit; + s[val_n] = '\0'; + exit: + //dprintf("< err=%d s=%s\n", err, s); + return err; +} + +int unmarshal_new_string(IOStream *io, char **s, uint32_t *s_n){ + int err = 0, val_n = 0; + char *val = NULL; + //dprintf(">\n"); + err = unmarshal_uint32(io, &val_n); + if(err) goto exit; + val = allocate(val_n + 1); + if(!val){ + err = -ENOMEM; + goto exit; + } + err = unmarshal_bytes(io, val, val_n); + if(err) goto exit; + val[val_n] = '\0'; + exit: + if(err){ + if(val) deallocate(val); + val = NULL; + val_n = 0; + } + *s = val; + if(s_n) *s_n = val_n; + //dprintf("< err=%d s=%s\n", err, *s); + return err; +} diff --git a/tools/xfrd/marshal.h b/tools/xfrd/marshal.h new file mode 100644 index 0000000000..65e9682cea --- /dev/null +++ b/tools/xfrd/marshal.h @@ -0,0 +1,42 @@ +#ifndef _XUTIL_MARSHAL_H_ +#define _XUTIL_MARSHAL_H_ + +#include "iostream.h" + +/** A 16-bit uint in network order, e.g. a port number. */ +typedef uint16_t net16_t; + +/** A 32-bit uint in network order, e.g. an IP address. */ +typedef uint32_t net32_t; + +extern int marshal_flush(IOStream *io); + +extern int marshal_bytes(IOStream *io, void *s, uint32_t s_n); +extern int unmarshal_bytes(IOStream *io, void *s, uint32_t s_n); + +extern int marshal_uint8(IOStream *io, uint8_t x); +extern int unmarshal_uint8(IOStream *io, uint8_t *x); + +extern int marshal_uint16(IOStream *io, uint16_t x); +extern int unmarshal_uint16(IOStream *io, uint16_t *x); + +extern int marshal_uint32(IOStream *io, uint32_t x); +extern int unmarshal_uint32(IOStream *io, uint32_t *x); + +extern int marshal_int32(IOStream *io, int32_t x); +extern int unmarshal_int32(IOStream *io, int32_t *x); + +extern int marshal_uint64(IOStream *io, uint64_t x); +extern int unmarshal_uint64(IOStream *io, uint64_t *x); + +extern int marshal_net16(IOStream *io, net16_t x); +extern int unmarshal_net16(IOStream *io, net16_t *x); + +extern int marshal_net32(IOStream *io, net32_t x); +extern int unmarshal_net32(IOStream *io, net32_t *x); + +extern int marshal_string(IOStream *io, char *s, uint32_t s_n); +extern int unmarshal_string(IOStream *io, char *s, uint32_t s_n); +extern int unmarshal_new_string(IOStream *io, char **s, uint32_t *s_n); + +#endif /* ! _XUTIL_MARSHAL_H_ */ diff --git a/tools/xfrd/select.c b/tools/xfrd/select.c new file mode 100644 index 0000000000..bdaccfe000 --- /dev/null +++ b/tools/xfrd/select.c @@ -0,0 +1,50 @@ +#include +#include +#include + +#include "select.h" + +/** Zero all the file descriptor sets. + * + * @param set select set + * @param fd file descriptor + * @return 0 on success, -1 otherwise + */ +void SelectSet_zero(SelectSet *set){ + set->n = 0; + FD_ZERO(&set->rd); + FD_ZERO(&set->wr); + FD_ZERO(&set->er); +} + +/** Add a file descriptor to the write set. + * + * @param set select set + * @param fd file descriptor + * @return 0 on success, -1 otherwise + */ +void SelectSet_add_read(SelectSet *set, int fd){ + FD_SET(fd, &set->rd); + if(fd > set->n) set->n = fd; +} + +/** Add a file descriptor to the write set. + * + * @param set select set + * @param fd file descriptor + * @return 0 on success, -1 otherwise + */ +void SelectSet_add_write(SelectSet *set, int fd){ + FD_SET(fd, &set->wr); + if(fd > set->n) set->n = fd; +} + +/** Select on file descriptors. + * + * @param set select set + * @param timeout timeout (may be NULL for no timeout) + * @return 0 on success, -1 otherwise + */ +int SelectSet_select(SelectSet *set, struct timeval *timeout){ + return select(set->n+1, &set->rd, &set->wr, &set->er, timeout); +} diff --git a/tools/xfrd/select.h b/tools/xfrd/select.h new file mode 100644 index 0000000000..2453f98536 --- /dev/null +++ b/tools/xfrd/select.h @@ -0,0 +1,16 @@ +#ifndef _XFRD_SELECT_H_ +#define _XFRD_SELECT_H_ + +/** Set of file descriptors for select. + */ +typedef struct SelectSet { + int n; + fd_set rd, wr, er; +} SelectSet; + +extern void SelectSet_zero(SelectSet *set); +extern void SelectSet_add_read(SelectSet *set, int fd); +extern void SelectSet_add_write(SelectSet *set, int fd); +extern int SelectSet_select(SelectSet *set, struct timeval *timeout); + +#endif /* ! _XFRD_SELECT_H_ */ diff --git a/tools/xfrd/sxpr.c b/tools/xfrd/sxpr.c new file mode 100644 index 0000000000..81fd14801c --- /dev/null +++ b/tools/xfrd/sxpr.c @@ -0,0 +1,950 @@ +/* + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. This library is + * distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include "sys_string.h" +#include "lexis.h" +#include "sys_net.h" +#include "hash_table.h" +#include "sxpr.h" + +#include +#undef free + +/** @file + * General representation of sxprs. + * Includes print, equal, and free functions for the sxpr types. + * + * Zero memory containing an Sxpr will have the value ONONE - this is intentional. + * When a function returning an sxpr cannot allocate memory we return ONOMEM. + * + */ + +static int atom_print(IOStream *io, Sxpr obj, unsigned flags); +static int atom_equal(Sxpr x, Sxpr y); +static void atom_free(Sxpr obj); + +static int string_print(IOStream *io, Sxpr obj, unsigned flags); +static int string_equal(Sxpr x, Sxpr y); +static void string_free(Sxpr obj); + +static int cons_print(IOStream *io, Sxpr obj, unsigned flags); +static int cons_equal(Sxpr x, Sxpr y); +static void cons_free(Sxpr obj); + +static int null_print(IOStream *io, Sxpr obj, unsigned flags); +static int none_print(IOStream *io, Sxpr obj, unsigned flags); +static int int_print(IOStream *io, Sxpr obj, unsigned flags); +static int bool_print(IOStream *io, Sxpr obj, unsigned flags); + +/** Type definitions. */ +static SxprType types[1024] = { + [T_NONE] { type: T_NONE, name: "none", print: none_print }, + [T_NULL] { type: T_NULL, name: "null", print: null_print }, + [T_UINT] { type: T_UINT, name: "int", print: int_print, }, + [T_BOOL] { type: T_BOOL, name: "bool", print: bool_print, }, + [T_ATOM] { type: T_ATOM, name: "atom", print: atom_print, + pointer: TRUE, + free: atom_free, + equal: atom_equal, + }, + [T_STRING] { type: T_STRING, name: "string", print: string_print, + pointer: TRUE, + free: string_free, + equal: string_equal, + }, + [T_CONS] { type: T_CONS, name: "cons", print: cons_print, + pointer: TRUE, + free: cons_free, + equal: cons_equal, + }, +}; + +/** Number of entries in the types array. */ +static int type_sup = sizeof(types)/sizeof(types[0]); + +/** Get the type definition for a given type code. + * + * @param ty type code + * @return type definition or null + */ +SxprType *get_sxpr_type(int ty){ + if(0 <= ty && ty < type_sup){ + return types+ty; + } + return NULL; +} + +/** The default print function. + * + * @param io stream to print to + * @param x sxpr to print + * @param flags print flags + * @return number of bytes written on success + */ +int default_print(IOStream *io, Sxpr x, unsigned flags){ + return IOStream_print(io, "#<%u %lu>\n", get_type(x), get_ul(x)); +} + +/** The default equal function. + * Uses eq(). + * + * @param x sxpr to compare + * @param y sxpr to compare + * @return 1 if equal, 0 otherwise + */ +int default_equal(Sxpr x, Sxpr y){ + return eq(x, y); +} + +/** General sxpr print function. + * Prints an sxpr on a stream using the print function for the sxpr type. + * Printing is controlled by flags from the PrintFlags enum. + * If PRINT_TYPE is in the flags the sxpr type is printed before the sxpr + * (for debugging). + * + * @param io stream to print to + * @param x sxpr to print + * @param flags print flags + * @return number of bytes written + */ +int objprint(IOStream *io, Sxpr x, unsigned flags){ + SxprType *def = get_sxpr_type(get_type(x)); + ObjPrintFn *print_fn = (def && def->print ? def->print : default_print); + int k = 0; + if(!io) return k; + if(flags & PRINT_TYPE){ + k += IOStream_print(io, "%s:", def->name); + } + k += print_fn(io, x, flags); + return k; +} + +/** General sxpr free function. + * Frees an sxpr using the free function for its type. + * Free functions must recursively free any subsxprs. + * If no function is defined then the default is to + * free sxprs whose type has pointer true. + * Sxprs must not be used after freeing. + * + * @param x sxpr to free + */ +void objfree(Sxpr x){ + SxprType *def = get_sxpr_type(get_type(x)); + + if(def){ + if(def->free){ + def->free(x); + } else if (def->pointer){ + hfree(x); + } + } +} + +/** General sxpr equality function. + * Compares x and y using the equal function for x. + * Uses default_equal() if x has no equal function. + * + * @param x sxpr to compare + * @param y sxpr to compare + * @return 1 if equal, 0 otherwise + */ +int objequal(Sxpr x, Sxpr y){ + SxprType *def = get_sxpr_type(get_type(x)); + ObjEqualFn *equal_fn = (def && def->equal ? def->equal : default_equal); + return equal_fn(x, y); +} + +/** Search for a key in an alist. + * An alist is a list of conses, where the cars + * of the conses are the keys. Compares keys using equality. + * + * @param k key + * @param l alist to search + * @return first element of l with car k, or ONULL + */ +Sxpr assoc(Sxpr k, Sxpr l){ + for( ; CONSP(l) ; l = CDR(l)){ + Sxpr x = CAR(l); + if(CONSP(x) && objequal(k, CAR(x))){ + return x; + } + } + return ONULL; +} + +/** Search for a key in an alist. + * An alist is a list of conses, where the cars + * of the conses are the keys. Compares keys using eq. + * + * @param k key + * @param l alist to search + * @return first element of l with car k, or ONULL + */ +Sxpr assocq(Sxpr k, Sxpr l){ + for( ; CONSP(l); l = CDR(l)){ + Sxpr x = CAR(l); + if(CONSP(x) && eq(k, CAR(x))){ + return x; + } + } + return ONULL; +} + +/** Add a new key and value to an alist. + * + * @param k key + * @param l value + * @param l alist + * @return l with the new cell added to the front + */ +Sxpr acons(Sxpr k, Sxpr v, Sxpr l){ + Sxpr x, y; + x = cons_new(k, v); + if(NOMEMP(x)) return x; + y = cons_new(x, l); + if(NOMEMP(y)) cons_free_cells(x); + return y; +} + +/** Test if a list contains an element. + * Uses sxpr equality. + * + * @param l list + * @param x element to look for + * @return a tail of l with x as car, or ONULL + */ +Sxpr cons_member(Sxpr l, Sxpr x){ + for( ; CONSP(l) && !eq(x, CAR(l)); l = CDR(l)){} + return l; +} + +/** Test if a list contains an element satisfying a test. + * The test function is called with v and an element of the list. + * + * @param l list + * @param test_fn test function to use + * @param v value for first argument to the test + * @return a tail of l with car satisfying the test, or 0 + */ +Sxpr cons_member_if(Sxpr l, ObjEqualFn *test_fn, Sxpr v){ + for( ; CONSP(l) && !test_fn(v, CAR(l)); l = CDR(l)){ } + return l; +} + +/** Test if the elements of list 't' are a subset of the elements + * of list 's'. Element order is not significant. + * + * @param s element list to check subset of + * @param t element list to check if is a subset + * @return 1 if is a subset, 0 otherwise + */ +int cons_subset(Sxpr s, Sxpr t){ + for( ; CONSP(t); t = CDR(t)){ + if(!CONSP(cons_member(s, CAR(t)))){ + return 0; + } + } + return 1; +} + +/** Test if two lists have equal sets of elements. + * Element order is not significant. + * + * @param s list to check + * @param t list to check + * @return 1 if equal, 0 otherwise + */ +int cons_set_equal(Sxpr s, Sxpr t){ + return cons_subset(s, t) && cons_subset(t, s); +} + +#ifdef USE_GC +/*============================================================================*/ +/* The functions inside this ifdef are only safe if GC is used. + * Otherwise they may leak memory. + */ + +/** Remove an element from a list (GC only). + * Uses sxpr equality and removes all instances, even + * if there are more than one. + * + * @param l list to remove elements from + * @param x element to remove + * @return modified input list + */ +Sxpr cons_remove(Sxpr l, Sxpr x){ + return cons_remove_if(l, eq, x); +} + +/** Remove elements satisfying a test (GC only). + * The test function is called with v and an element of the set. + * + * @param l list to remove elements from + * @param test_fn function to use to decide if an element should be removed + * @return modified input list + */ +Sxpr cons_remove_if(Sxpr l, ObjEqualFn *test_fn, Sxpr v){ + Sxpr prev = ONULL, elt, next; + + for(elt = l; CONSP(elt); elt = next){ + next = CDR(elt); + if(test_fn(v, CAR(elt))){ + if(NULLP(prev)){ + l = next; + } else { + CDR(prev) = next; + } + } + } + return l; +} + +/** Set the value for a key in an alist (GC only). + * If the key is present, changes the value, otherwise + * adds a new cell. + * + * @param k key + * @param v value + * @param l alist + * @return modified or extended list + */ +Sxpr setf(Sxpr k, Sxpr v, Sxpr l){ + Sxpr e = assoc(k, l); + if(NULLP(e)){ + l = acons(k, v, l); + } else { + CAR(CDR(e)) = v; + } + return l; +} +/*============================================================================*/ +#endif /* USE_GC */ + +/** Create a new atom with the given name. + * + * @param name the name + * @return new atom + */ +Sxpr atom_new(char *name){ + Sxpr n, obj = ONOMEM; + + n = string_new(name); + if(NOMEMP(n)) goto exit; + obj = HALLOC(ObjAtom, T_ATOM); + if(NOMEMP(obj)) goto exit; + OBJ_ATOM(obj)->name = n; + exit: + return obj; +} + +/** Free an atom. + * + * @param obj to free + */ +void atom_free(Sxpr obj){ + // Interned atoms are shared, so do not free. + if(OBJ_ATOM(obj)->interned) return; + objfree(OBJ_ATOM(obj)->name); + hfree(obj); +} + +/** Print an atom. Prints the atom name. + * + * @param io stream to print to + * @param obj to print + * @param flags print flags + * @return number of bytes printed + */ +int atom_print(IOStream *io, Sxpr obj, unsigned flags){ + //return string_print(io, OBJ_ATOM(obj)->name, (flags | PRINT_RAW)); + return string_print(io, OBJ_ATOM(obj)->name, flags); +} + +/** Atom equality. + * + * @param x to compare + * @param y to compare + * @return 1 if equal, 0 otherwise + */ +int atom_equal(Sxpr x, Sxpr y){ + int ok; + ok = eq(x, y); + if(ok) goto exit; + ok = ATOMP(y) && string_equal(OBJ_ATOM(x)->name, OBJ_ATOM(y)->name); + if(ok) goto exit; + ok = STRINGP(y) && string_equal(OBJ_ATOM(x)->name, y); + exit: + return ok; +} + +/** Get the name of an atom. + * + * @param obj atom + * @return name + */ +char * atom_name(Sxpr obj){ + return string_string(OBJ_ATOM(obj)->name); +} + +/** Get the C string from a string sxpr. + * + * @param obj string sxpr + * @return string + */ +char * string_string(Sxpr obj){ + return OBJ_STRING(obj); +} + +/** Get the length of a string. + * + * @param obj string + * @return length + */ +int string_length(Sxpr obj){ + return strlen(OBJ_STRING(obj)); +} + +/** Create a new string. The input string is copied, + * and must be null-terminated. + * + * @param s characters to put in the string + * @return new sxpr + */ +Sxpr string_new(char *s){ + int n = (s ? strlen(s) : 0); + Sxpr obj; + obj = halloc(n+1, T_STRING); + if(!NOMEMP(obj)){ + char *str = OBJ_STRING(obj); + strncpy(str, s, n); + str[n] = '\0'; + } + return obj; +} + +/** Free a string. + * + * @param obj to free + */ +void string_free(Sxpr obj){ + hfree(obj); +} + +/** Determine if a string needs escapes when printed + * using the given flags. + * + * @param str string to check + * @param flags print flags + * @return 1 if needs escapes, 0 otherwise + */ +int needs_escapes(char *str, unsigned flags){ + char *c; + int val = 0; + + if(str){ + for(c=str; *c; c++){ + if(in_alpha_class(*c)) continue; + if(in_decimal_digit_class(*c)) continue; + if(in_class(*c, "/._+:@~-")) continue; + val = 1; + break; + } + } + //printf("\n> val=%d str=|%s|\n", val, str); + return val; +} + +/** Print a string to a stream, with escapes if necessary. + * + * @param io stream to print to + * @param str string + * @param flags print flags + * @return number of bytes written + */ +int _string_print(IOStream *io, char *str, unsigned flags){ + int k = 0; + if((flags & PRINT_RAW) || !needs_escapes(str, flags)){ + k += IOStream_print(io, str); + } else { + k += IOStream_print(io, "\""); + if(str){ + char *s; + for(s = str; *s; s++){ + if(*s < ' ' || *s >= 127 ){ + switch(*s){ + case '\a': k += IOStream_print(io, "\\a"); break; + case '\b': k += IOStream_print(io, "\\b"); break; + case '\f': k += IOStream_print(io, "\\f"); break; + case '\n': k += IOStream_print(io, "\\n"); break; + case '\r': k += IOStream_print(io, "\\r"); break; + case '\t': k += IOStream_print(io, "\\t"); break; + case '\v': k += IOStream_print(io, "\\v"); break; + default: + // Octal escape; + k += IOStream_print(io, "\\%o", *s); + break; + } + } else if(*s == c_double_quote || + *s == c_single_quote || + *s == c_escape){ + k += IOStream_print(io, "\\%c", *s); + } else { + k+= IOStream_print(io, "%c", *s); + } + } + } + k += IOStream_print(io, "\""); + } + return k; +} + +/** Print a string to a stream, with escapes if necessary. + * + * @param io stream to print to + * @param obj string + * @param flags print flags + * @return number of bytes written + */ +int string_print(IOStream *io, Sxpr obj, unsigned flags){ + return _string_print(io, OBJ_STRING(obj), flags); +} + +/** Compare an sxpr with a string for equality. + * + * @param x string to compare with + * @param y sxpr to compare + * @return 1 if equal, 0 otherwise + */ +int string_equal(Sxpr x, Sxpr y){ + int ok = 0; + ok = eq(x,y); + if(ok) goto exit; + ok = has_type(y, T_STRING) && !strcmp(OBJ_STRING(x), OBJ_STRING(y)); + if(ok) goto exit; + ok = has_type(y, T_ATOM) && !strcmp(OBJ_STRING(x), atom_name(y)); + exit: + return ok; +} + +/** Create a new cons cell. + * The cell is ONOMEM if either argument is. + * + * @param car sxpr for the car + * @param cdr sxpr for the cdr + * @return new cons + */ +Sxpr cons_new(Sxpr car, Sxpr cdr){ + Sxpr obj; + if(NOMEMP(car) || NOMEMP(cdr)){ + obj = ONOMEM; + } else { + obj = HALLOC(ObjCons, T_CONS); + if(!NOMEMP(obj)){ + ObjCons *z = OBJ_CONS(obj); + z->car = car; + z->cdr = cdr; + } + } + return obj; +} + +/** Push a new element onto a list. + * + * @param list list to add to + * @param elt element to add + * @return 0 if successful, error code otherwise + */ +int cons_push(Sxpr *list, Sxpr elt){ + Sxpr l; + l = cons_new(elt, *list); + if(NOMEMP(l)) return -ENOMEM; + *list = l; + return 0; +} + +/** Free a cons. Recursively frees the car and cdr. + * + * @param obj to free + */ +void cons_free(Sxpr obj){ + Sxpr next; + for(; CONSP(obj); obj = next){ + next = CDR(obj); + objfree(CAR(obj)); + hfree(obj); + } + if(!NULLP(obj)){ + objfree(obj); + } +} + +/** Free a cons and its cdr cells, but not the car sxprs. + * Does nothing if called on something that is not a cons. + * + * @param obj to free + */ +void cons_free_cells(Sxpr obj){ + Sxpr next; + for(; CONSP(obj); obj = next){ + next = CDR(obj); + hfree(obj); + } +} + +/** Print a cons. + * Prints the cons in list format if the cdrs are conses. + * uses pair (dot) format if the last cdr is not a cons (or null). + * + * @param io stream to print to + * @param obj to print + * @param flags print flags + * @return number of bytes written + */ +int cons_print(IOStream *io, Sxpr obj, unsigned flags){ + int first = 1; + int k = 0; + k += IOStream_print(io, "("); + for( ; CONSP(obj) ; obj = CDR(obj)){ + if(first){ + first = 0; + } else { + k += IOStream_print(io, " "); + } + k += objprint(io, CAR(obj), flags); + } + if(!NULLP(obj)){ + k += IOStream_print(io, " . "); + k += objprint(io, obj, flags); + } + k += IOStream_print(io, ")"); + return (IOStream_error(io) ? -1 : k); +} + +/** Compare a cons with another sxpr for equality. + * If y is a cons, compares the cars and cdrs recursively. + * + * @param x cons to compare + * @param y sxpr to compare + * @return 1 if equal, 0 otherwise + */ +int cons_equal(Sxpr x, Sxpr y){ + return CONSP(y) && + objequal(CAR(x), CAR(y)) && + objequal(CDR(x), CDR(y)); +} + +/** Return the length of a cons list. + * + * @param obj list + * @return length + */ +int cons_length(Sxpr obj){ + int count = 0; + for( ; CONSP(obj); obj = CDR(obj)){ + count++; + } + return count; +} + +/** Destructively reverse a cons list in-place. + * If the argument is not a cons it is returned unchanged. + * + * @param l to reverse + * @return reversed list + */ +Sxpr nrev(Sxpr l){ + if(CONSP(l)){ + // Iterate down the cells in the list making the cdr of + // each cell point to the previous cell. The last cell + // is the head of the reversed list. + Sxpr prev = ONULL; + Sxpr cell = l; + Sxpr next; + + while(1){ + next = CDR(cell); + CDR(cell) = prev; + if(!CONSP(next)) break; + prev = cell; + cell = next; + } + l = cell; + } + return l; +} + +/** Print the null sxpr. + * + * @param io stream to print to + * @param obj to print + * @param flags print flags + * @return number of bytes written + */ +static int null_print(IOStream *io, Sxpr obj, unsigned flags){ + return IOStream_print(io, "()"); +} + +/** Print the `unspecified' sxpr none. + * + * @param io stream to print to + * @param obj to print + * @param flags print flags + * @return number of bytes written + */ +static int none_print(IOStream *io, Sxpr obj, unsigned flags){ + return IOStream_print(io, ""); +} + +/** Print an integer. + * + * @param io stream to print to + * @param obj to print + * @param flags print flags + * @return number of bytes written + */ +static int int_print(IOStream *io, Sxpr obj, unsigned flags){ + return IOStream_print(io, "%d", OBJ_INT(obj)); +} + +/** Print a boolean. + * + * @param io stream to print to + * @param obj to print + * @param flags print flags + * @return number of bytes written + */ +static int bool_print(IOStream *io, Sxpr obj, unsigned flags){ + return IOStream_print(io, (OBJ_UINT(obj) ? k_true : k_false)); +} + +int sxprp(Sxpr obj, Sxpr name){ + return CONSP(obj) && objequal(CAR(obj), name); +} + +/** Get the name of an element. + * + * @param obj element + * @return name + */ +Sxpr sxpr_name(Sxpr obj){ + Sxpr val = ONONE; + if(CONSP(obj)){ + val = CAR(obj); + } else if(STRINGP(obj) || ATOMP(obj)){ + val = obj; + } + return val; +} + +int sxpr_is(Sxpr obj, char *s){ + if(ATOMP(obj)) return !strcmp(atom_name(obj), s); + if(STRINGP(obj)) return !strcmp(string_string(obj), s); + return 0; +} + +int sxpr_elementp(Sxpr obj, Sxpr name){ + int ok = 0; + ok = CONSP(obj) && objequal(CAR(obj), name); + return ok; +} + +/** Get the attributes of an sxpr. + * + * @param obj sxpr + * @return attributes + */ +Sxpr sxpr_attributes(Sxpr obj){ + Sxpr val = ONULL; + if(CONSP(obj)){ + obj = CDR(obj); + if(CONSP(obj)){ + obj = CAR(obj); + if(sxprp(obj, intern("@"))){ + val = CDR(obj); + } + } + } + return val; +} + +Sxpr sxpr_attribute(Sxpr obj, Sxpr key, Sxpr def){ + Sxpr val = ONONE; + val = assoc(sxpr_attributes(obj), key); + if(CONSP(val) && CONSP(CDR(val))){ + val = CADR(def); + } else { + val = def; + } + return val; +} + +/** Get the children of an sxpr. + * + * @param obj sxpr + * @return children + */ +Sxpr sxpr_children(Sxpr obj){ + Sxpr val = ONULL; + if(CONSP(obj)){ + val = CDR(obj); + if(CONSP(val) && sxprp(CAR(val), intern("@"))){ + val = CDR(val); + } + } + return val; +} + +Sxpr sxpr_child(Sxpr obj, Sxpr name, Sxpr def){ + Sxpr val = ONONE; + Sxpr l; + for(l = sxpr_children(obj); CONSP(l); l = CDR(l)){ + if(sxprp(CAR(l), name)){ + val = CAR(l); + break; + } + } + if(NONEP(val)) val = def; + return val; +} + +Sxpr sxpr_child0(Sxpr obj, Sxpr def){ + Sxpr val = ONONE; + Sxpr l = sxpr_children(obj); + if(CONSP(l)){ + val = CAR(l); + } else { + val = def; + } + return val; +} + +Sxpr sxpr_childN(Sxpr obj, int n, Sxpr def){ + Sxpr val = def; + Sxpr l; + int i; + for (i = 0, l = sxpr_children(obj); CONSP(l); i++, l = CDR(l)){ + if(i == n){ + val = CAR(l); + break; + } + } + return val; +} + +Sxpr sxpr_child_value(Sxpr obj, Sxpr name, Sxpr def){ + Sxpr val = ONONE; + val = sxpr_child(obj, name, ONONE); + if(NONEP(val)){ + val = def; + } else { + val = sxpr_child0(val, def); + } + return val; +} + +/** Table of interned symbols. Indexed by symbol name. */ +static HashTable *symbols = NULL; + +/** Hash function for entries in the symbol table. + * + * @param key to hash + * @return hashcode + */ +static Hashcode sym_hash_fn(void *key){ + return hash_string((char*)key); +} + +/** Key equality function for the symbol table. + * + * @param x to compare + * @param y to compare + * @return 1 if equal, 0 otherwise + */ +static int sym_equal_fn(void *x, void *y){ + return !strcmp((char*)x, (char*)y); +} + +/** Entry free function for the symbol table. + * + * @param table the entry is in + * @param entry being freed + */ +static void sym_free_fn(HashTable *table, HTEntry *entry){ + if(entry){ + objfree(((ObjAtom*)entry->value)->name); + HTEntry_free(entry); + } +} + +/** Initialize the symbol table. + * + * @return 0 on sucess, error code otherwise + */ +static int init_symbols(void){ + symbols = HashTable_new(100); + if(symbols){ + symbols->key_hash_fn = sym_hash_fn; + symbols->key_equal_fn = sym_equal_fn; + symbols->entry_free_fn = sym_free_fn; + return 0; + } + return -1; +} + +/** Cleanup the symbol table. Frees the table and all its symbols. + */ +void cleanup_symbols(void){ + HashTable_free(symbols); + symbols = NULL; +} + +/** Get the interned symbol with the given name. + * No new symbol is created. + * + * @return symbol or null + */ +Sxpr get_symbol(char *sym){ + HTEntry *entry; + if(!symbols){ + if(init_symbols()) return ONOMEM; + return ONULL; + } + entry = HashTable_get_entry(symbols, sym); + if(entry){ + return OBJP(T_ATOM, entry->value); + } else { + return ONULL; + } +} + +/** Get the interned symbol with the given name. + * Creates a new symbol if necessary. + * + * @return symbol + */ +Sxpr intern(char *sym){ + Sxpr symbol = get_symbol(sym); + if(NULLP(symbol)){ + if(!symbols) return ONOMEM; + symbol = atom_new(sym); + if(!NOMEMP(symbol)){ + OBJ_ATOM(symbol)->interned = TRUE; + HashTable_add(symbols, atom_name(symbol), get_ptr(symbol)); + } + } + return symbol; +} diff --git a/tools/xfrd/sxpr.h b/tools/xfrd/sxpr.h new file mode 100644 index 0000000000..a5875c76bd --- /dev/null +++ b/tools/xfrd/sxpr.h @@ -0,0 +1,414 @@ +/* + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. This library is + * distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef _XUTIL_SXPR_H_ +#define _XUTIL_SXPR_H_ + +#include + +#include "hash_table.h" +#include "iostream.h" +#include "allocate.h" + +/** @file + * Definitions for rules and sxprs. + */ + +#ifndef NULL +#define NULL 0 +#endif + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +/** Sxpr type. */ +typedef int16_t TypeCode; + +/** A typed sxpr handle.*/ +typedef struct Sxpr { + /** Sxpr type. */ + TypeCode type; + union { + /** Sxpr value. */ + unsigned long ul; + /** Pointer. */ + void *ptr; + } v; +} Sxpr; + +/** Sxpr type to indicate out of memory. */ +#define T_NOMEM ((TypeCode)-1) +/** The 'unspecified' sxpr. */ +#define T_NONE ((TypeCode)0) +/** The empty list. */ +#define T_NULL ((TypeCode)1) +/** Unsigned integer. */ +#define T_UINT ((TypeCode)2) +/** A string. */ +#define T_STRING ((TypeCode)3) +/** An atom. */ +#define T_ATOM ((TypeCode)4) +/** A boolean. */ +#define T_BOOL ((TypeCode)5) + +/** A cons (pair or list). */ +#define T_CONS ((TypeCode)10) + +/** An error. */ +#define T_ERR ((TypeCode)40) + +/** An atom. */ +typedef struct ObjAtom { + Sxpr name; + Hashcode hashcode; + int interned; +} ObjAtom; + +/** A cons (pair). */ +typedef struct ObjCons { + Sxpr car; + Sxpr cdr; +} ObjCons; + +/** A vector. */ +typedef struct ObjVector { + int n; + Sxpr data[0]; +} ObjVector; + +/** Flags for sxpr printing. */ +enum PrintFlags { + PRINT_RAW = 0x001, + PRINT_TYPE = 0x002, + PRINT_PRETTY = 0x004, + PRINT_NUM = 0x008, +}; + +/** An integer sxpr. + * + * @param ty type + * @param val integer value + */ +#define OBJI(ty, val) (Sxpr){ type: (ty), v: { ul: (val) }} + +/** A pointer sxpr. + * If the pointer is non-null, returns an sxpr containing it. + * If the pointer is null, returns ONOMEM. + * + * @param ty type + * @param val pointer + */ +#define OBJP(ty, val) ((val) ? (Sxpr){ type: (ty), v: { ptr: (val) }} : ONOMEM) + +/** Make an integer sxpr containing a pointer. + * + * @param val pointer + */ +#define PTR(val) OBJP(T_UINT, (void*)(val)) + +/** Make an integer sxpr. + * @param x value + */ +#define OINT(x) OBJI(T_UINT, x) + +/** Make an error sxpr. + * + * @param x value + */ +#define OERR(x) OBJI(T_ERR, x) + +/** Out of memory constant. */ +#define ONOMEM OBJI(T_NOMEM, 0) + +/** The `unspecified' constant. */ +#define ONONE OBJI(T_NONE, 0) + +/** Empty list constant. */ +#define ONULL OBJI(T_NULL, 0) + +/** False constant. */ +#define OFALSE OBJI(T_BOOL, 0) + +/** True constant. */ +#define OTRUE OBJI(T_BOOL, 1) + +/* Recognizers for the various sxpr types. */ +#define ATOMP(obj) has_type(obj, T_ATOM) +#define BOOLP(obj) has_type(obj, T_BOOL) +#define CONSP(obj) has_type(obj, T_CONS) +#define ERRP(obj) has_type(obj, T_ERR) +#define INTP(obj) has_type(obj, T_UINT) +#define NOMEMP(obj) has_type(obj, T_NOMEM) +#define NONEP(obj) has_type(obj, T_NONE) +#define NULLP(obj) has_type(obj, T_NULL) +#define STRINGP(obj) has_type(obj, T_STRING) + +#define TRUEP(obj) get_ul(obj) + +/** Convert an sxpr to an unsigned integer. */ +#define OBJ_UINT(x) get_ul(x) +/** Convert an sxpr to an integer. */ +#define OBJ_INT(x) (int)get_ul(x) + +/* Conversions of sxprs to their values. + * No checking is done. + */ +#define OBJ_STRING(x) ((char*)get_ptr(x)) +#define OBJ_CONS(x) ((ObjCons*)get_ptr(x)) +#define OBJ_ATOM(x) ((ObjAtom*)get_ptr(x)) +#define OBJ_SET(x) ((ObjSet*)get_ptr(x)) +#define CAR(x) (OBJ_CONS(x)->car) +#define CDR(x) (OBJ_CONS(x)->cdr) + +#define CAAR(x) (CAR(CAR(x))) +#define CADR(x) (CAR(CDR(x))) +#define CDAR(x) (CDR(CAR(x))) +#define CDDR(x) (CDR(CDR(x))) + +/** Get the integer value from an sxpr. + * + * @param obj sxpr + * @return value + */ +static inline unsigned long get_ul(Sxpr obj){ + return obj.v.ul; +} + +/** Get the pointer value from an sxpr. + * + * @param obj sxpr + * @return value + */ +static inline void * get_ptr(Sxpr obj){ + return obj.v.ptr; +} + +/** Create an sxpr containing a pointer. + * + * @param type typecode + * @param val pointer + * @return sxpr + */ +static inline Sxpr obj_ptr(TypeCode type, void *val){ + return (Sxpr){ type: type, v: { ptr: val } }; +} + +/** Create an sxpr containing an integer. + * + * @param type typecode + * @param val integer + * @return sxpr + */ +static inline Sxpr obj_ul(TypeCode type, unsigned long val){ + return (Sxpr){ type: type, v: { ul: val } }; +} + +/** Get the type of an sxpr. + * + * @param obj sxpr + * @return type + */ +static inline TypeCode get_type(Sxpr obj){ + return obj.type; +} + +/** Check the type of an sxpr. + * + * @param obj sxpr + * @param type to check + * @return 1 if has the type, 0 otherwise + */ +static inline int has_type(Sxpr obj, TypeCode type){ + return get_type(obj) == type; +} + +/** Compare sxprs for literal equality of type and value. + * + * @param x sxpr to compare + * @param y sxpr to compare + * @return 1 if equal, 0 otherwise + */ +static inline int eq(Sxpr x, Sxpr y){ + return ((get_type(x) == get_type(y)) && (get_ul(x) == get_ul(y))); +} + +/** Checked version of CAR + * + * @param x sxpr + * @return CAR if a cons, x otherwise + */ +static inline Sxpr car(Sxpr x){ + return (CONSP(x) ? CAR(x) : x); +} + +/** Checked version of CDR. + * + * @param x sxpr + * @return CDR if a cons, null otherwise + */ +static inline Sxpr cdr(Sxpr x){ + return (CONSP(x) ? CDR(x) : ONULL); +} + +/** Allocate some memory and return an sxpr containing it. + * Returns ONOMEM if allocation failed. + * + * @param n number of bytes to allocate + * @param ty typecode + * @return sxpr + */ +static inline Sxpr halloc(size_t n, TypeCode ty){ + return OBJP(ty, allocate(n)); +} + +/** Allocate an sxpr containing a pointer to the given type. + * + * @param ty type (uses sizeof to determine how many bytes to allocate) + * @param code typecode + * @return sxpr, ONOMEM if allocation failed + */ +#define HALLOC(ty, code) halloc(sizeof(ty), code) + +typedef int ObjPrintFn(IOStream *io, Sxpr obj, unsigned flags); +typedef int ObjEqualFn(Sxpr obj, Sxpr other); +typedef void ObjFreeFn(Sxpr obj); + +/** An sxpr type definition. */ +typedef struct SxprType { + TypeCode type; + char *name; + int pointer; + ObjPrintFn *print; + ObjEqualFn *equal; + ObjFreeFn *free; +} SxprType; + + +extern SxprType *get_sxpr_type(int ty); + +/** Free the pointer in an sxpr. + * + * @param x sxpr containing a pointer + */ +static inline void hfree(Sxpr x){ + deallocate(get_ptr(x)); +} + +extern int objprint(IOStream *io, Sxpr x, unsigned flags); +extern int objequal(Sxpr x, Sxpr y); +extern void objfree(Sxpr x); + +extern void cons_free_cells(Sxpr obj); +extern Sxpr intern(char *s); + +extern Sxpr assoc(Sxpr k, Sxpr l); +extern Sxpr assocq(Sxpr k, Sxpr l); +extern Sxpr acons(Sxpr k, Sxpr v, Sxpr l); +extern Sxpr nrev(Sxpr l); +extern Sxpr cons_member(Sxpr l, Sxpr x); +extern Sxpr cons_member_if(Sxpr l, ObjEqualFn *test_fn, Sxpr v); +extern int cons_subset(Sxpr s, Sxpr t); +extern int cons_set_equal(Sxpr s, Sxpr t); + +#ifdef USE_GC +extern Sxpr cons_remove(Sxpr l, Sxpr x); +extern Sxpr cons_remove_if(Sxpr l, ObjEqualFn *test_fn, Sxpr v); +#endif + +extern Sxpr atom_new(char *name); +extern char * atom_name(Sxpr obj); + +extern Sxpr string_new(char *s); +extern char * string_string(Sxpr obj); +extern int string_length(Sxpr obj); + +extern Sxpr cons_new(Sxpr car, Sxpr cdr); +extern int cons_push(Sxpr *list, Sxpr elt); +extern int cons_length(Sxpr obj); + +Sxpr sxpr_name(Sxpr obj); +int sxpr_is(Sxpr obj, char *s); +int sxpr_elementp(Sxpr obj, Sxpr name); +Sxpr sxpr_attributes(Sxpr obj); +Sxpr sxpr_attribute(Sxpr obj, Sxpr key, Sxpr def); +Sxpr sxpr_children(Sxpr obj); +Sxpr sxpr_child(Sxpr obj, Sxpr name, Sxpr def); +Sxpr sxpr_childN(Sxpr obj, int n, Sxpr def); +Sxpr sxpr_child0(Sxpr obj, Sxpr def); +Sxpr sxpr_child_value(Sxpr obj, Sxpr name, Sxpr def); + +/** Create a new atom. + * + * @param s atom name + * @return new atom + */ +static inline Sxpr mkatom(char *s){ + return atom_new(s); +} + +/** Create a new string sxpr. + * + * @param s string bytes (copied) + * @return new string + */ +static inline Sxpr mkstring(char *s){ + return string_new(s); +} + +/** Create an integer sxpr. + * + * @param i value + * @return sxpr + */ +static inline Sxpr mkint(int i){ + return OBJI(T_UINT, i); +} + +/** Create a boolean sxpr. + * + * @param b value + * @return sxpr + */ +static inline Sxpr mkbool(int b){ + return OBJI(T_BOOL, (b ? 1 : 0)); +} + +/* Constants used in parsing and printing. */ +#define k_list_open "(" +#define c_list_open '(' +#define k_list_close ")" +#define c_list_close ')' +#define k_true "true" +#define k_false "false" + +#define c_var '$' +#define c_escape '\\' +#define c_single_quote '\'' +#define c_double_quote '"' +#define c_string_open c_double_quote +#define c_string_close c_double_quote +#define c_data_open '[' +#define c_data_close ']' +#define c_binary '*' +#define c_eval '!' +#define c_concat_open '{' +#define c_concat_close '}' + +#endif /* ! _XUTIL_SXPR_H_ */ diff --git a/tools/xfrd/xdr.c b/tools/xfrd/xdr.c new file mode 100644 index 0000000000..61dbe44ed6 --- /dev/null +++ b/tools/xfrd/xdr.c @@ -0,0 +1,316 @@ +#include +#include "xdr.h" + +#define MODULE_NAME "XDR" +//#define DEBUG 1 +#undef DEBUG +#include "debug.h" + +/** @file + * XDR packer/unpacker for elements. + * + * string -> [T_STRING] [len:u16] + * atom -> [T_ATOM] [len:u16] + * uint -> [T_UINT] [value] + * cons -> [T_LIST] {1 elt}* 0 + * null -> [T_NULL] + * none -> [T_NONE] + * bool -> [T_BOOL] { 0:u8 | 1:u8 } + * + * types packed as u16. + * + * So (a b c) -> [T_CONS] a [T_CONS] b [T_CONS] c [T_NULL] + * () -> [T_NULL] + */ + +int pack_bool(IOStream *io, int x){ + int err=0; + //dprintf("> x=%d\n", x); + err = IOStream_print(io, "%c", 0xff & x); + if(err > 0) err = 0; + //dprintf("< err=%d\n", err); + return err; +} + +int unpack_bool(IOStream *io, int *x){ + int err = 0; + int c; + //dprintf(">\n"); + c = IOStream_getc(io); + *x = (c < 0 ? 0 : c); + err = IOStream_error(io); + if(c < 0 && !err) err = -EIO; + //dprintf("< err=%d x=%d\n", err, *x); + return err; +} + +int pack_ushort(IOStream *io, unsigned short x){ + int err=0; + //dprintf("> x=%u\n", x); + err = IOStream_print(io, "%c%c", + 0xff & (x >> 8), + 0xff & (x )); + if(err > 0) err = 0; + //dprintf("< err=%d\n", err); + return err; +} + +int unpack_ushort(IOStream *io, unsigned short *x){ + int err = 0; + int i, c = 0; + //dprintf(">\n"); + *x = 0; + for(i = 0; i< 2; i++){ + c = IOStream_getc(io); + if(c < 0) break; + *x <<= 8; + *x |= (0xff & c); + } + err = IOStream_error(io); + + if(c < 0 && !err) err = -EIO; + //dprintf("< err=%d x=%u\n", err, *x); + return err; +} + +int pack_type(IOStream *io, unsigned short x){ + return pack_ushort(io, x); +} + +int unpack_type(IOStream *io, unsigned short *x){ + return unpack_ushort(io, x); +} + +int pack_uint(IOStream *io, unsigned int x){ + int err=0; + //dprintf("> x=%u\n", x); + err = IOStream_print(io, "%c%c%c%c", + 0xff & (x >> 24), + 0xff & (x >> 16), + 0xff & (x >> 8), + 0xff & (x )); + if(err > 0) err = 0; + //dprintf("< err=%d\n", err); + return err; +} + +int unpack_uint(IOStream *io, unsigned int *x){ + int err = 0; + int i, c = 0; + //dprintf(">\n"); + *x = 0; + for(i = 0; i< 4; i++){ + c = IOStream_getc(io); + if(c < 0) break; + *x <<= 8; + *x |= (0xff & c); + } + err = IOStream_error(io); + if(c < 0 && !err) err = -EIO; + //dprintf("< err=%d x=%u\n", err, *x); + return err; +} + +int pack_string(IOStream *io, Sxpr x){ + int err = 0; + unsigned short n = 0xffff & string_length(x); + char *s = string_string(x); + int i; + //dprintf("> n=%d s=%s\n", n, s); + err = pack_ushort(io, n); + if(err) goto exit; + for(i = 0; i < n; i++){ + err = IOStream_print(io, "%c", s[i]); + if(err < 0) break; + } + if(err > 0) err = 0; + exit: + //dprintf("< err=%d\n", err); + return err; +} + +int unpack_string(IOStream *io, Sxpr *x){ + int err; + unsigned short n; + int i, c = 0; + char *s; + Sxpr val = ONONE; + + //dprintf(">\n"); + err = unpack_ushort(io, &n); + if(err) goto exit; + val = halloc(n+1, T_STRING); + if(NOMEMP(val)){ + err = -ENOMEM; + goto exit; + } + s = string_string(val); + for(i=0; i\n"); + for(l = x; CONSP(l); l = CDR(l)){ + err = pack_bool(io, 1); + if(err) goto exit; + err = pack_sxpr(io, CAR(l)); + if(err) goto exit; + } + err = pack_bool(io, 0); + exit: + //dprintf("< err=%d\n", err); + return err; +} + +int unpack_cons(IOStream *io, Sxpr *x){ + int err = 0; + int more = 0; + Sxpr u = ONONE, v = ONONE, val = ONULL; + + dprintf(">\n"); + while(1){ + err = unpack_bool(io, &more); + if(err) goto exit; + if(!more){ + //IOStream_print(iostdout, "unpack_cons 1 val="); + ////objprint(iostdout, val, 0); + IOStream_print(iostdout, "\n"); + + val = nrev(val); + + //IOStream_print(iostdout, "unpack_cons 2 val="); + //objprint(iostdout, val, 0); + //IOStream_print(iostdout, "\n"); + + break; + } + err = unpack_sxpr(io, &u); + if(err) goto exit; + v = cons_new(u, val); + if(NOMEMP(v)){ + err = -ENOMEM; + objfree(u); + goto exit; + } + val = v; + } + exit: + if(err){ + objfree(val); + val = ONONE; + } + *x = val; + dprintf("< err=%d\n", err); + return err; +} + +int pack_sxpr(IOStream *io, Sxpr x){ + int err = 0; + unsigned short type = get_type(x); + //dprintf(">\n"); + //objprint(iostdout, x, 0); + //IOStream_print(iostdout, "\n"); + + err = pack_type(io, type); + if(err) goto exit; + switch(type){ + case T_NULL: + break; + case T_NONE: + break; + case T_BOOL: + err = pack_bool(io, get_ul(x)); + break; + case T_CONS: + err = pack_cons(io, x); + break; + case T_ATOM: + err = pack_string(io, OBJ_ATOM(x)->name); + break; + case T_STRING: + err = pack_string(io, x); + break; + case T_UINT: + err = pack_uint(io, get_ul(x)); + break; + default: + err = -EINVAL; + IOStream_print(iostderr, "%s> invalid type %d\n", __FUNCTION__, type); + break; + } + exit: + //dprintf("< err=%d\n", err); + return err; +} + +int unpack_sxpr(IOStream *io, Sxpr *x){ + int err = 0; + unsigned short type; + unsigned int u; + Sxpr val = ONONE, y; + + //dprintf(">\n"); + err = unpack_type(io, &type); + if(err) goto exit; + switch(type){ + case T_NULL: + val = ONULL; + break; + case T_NONE: + val = ONONE; + break; + case T_CONS: + err = unpack_cons(io, &val); + break; + case T_BOOL: + err = unpack_bool(io, &u); + if(err) goto exit; + val = (u ? OTRUE : OFALSE); + break; + case T_ATOM: + err = unpack_string(io, &y); + if(err) goto exit; + val = intern(string_string(y)); + objfree(y); + break; + case T_STRING: + err = unpack_string(io, &val); + break; + case T_UINT: + err = unpack_uint(io, &u); + if(err) goto exit; + val = OBJI(type, u); + break; + default: + err = -EINVAL; + IOStream_print(iostderr, "%s> invalid type %d\n", __FUNCTION__, type); + break; + } + exit: + *x = (err ? ONONE : val); + //IOStream_print(iostdout, "sxpr="); + //objprint(iostdout, *x, 0); + //IOStream_print(iostdout, "\n"); + //dprintf("< err=%d\n", err); + return err; +} diff --git a/tools/xfrd/xdr.h b/tools/xfrd/xdr.h new file mode 100644 index 0000000000..793cd34a29 --- /dev/null +++ b/tools/xfrd/xdr.h @@ -0,0 +1,30 @@ +#ifndef _XUTIL_XDR_H_ +#define _XUTIL_XDR_H_ +#include "iostream.h" +#include "sxpr.h" + +int pack_type(IOStream *io, unsigned short x); + +int unpack_type(IOStream *io, unsigned short *x); + +int pack_bool(IOStream *io, int x); + +int unpack_bool(IOStream *io, int *x); + +int pack_uint(IOStream *out, unsigned int x); + +int unpack_uint(IOStream *in, unsigned int *x); + +int pack_string(IOStream *out, Sxpr x); + +int unpack_string(IOStream *in, Sxpr *x); + +int pack_cons(IOStream *out, Sxpr x); + +int unpack_cons(IOStream *in, Sxpr *x); + +int pack_sxpr(IOStream *out, Sxpr x); + +int unpack_sxpr(IOStream *in, Sxpr *x); + +#endif /* _XUTIL_XDR_H_ */ diff --git a/tools/xfrd/xen_domain.c b/tools/xfrd/xen_domain.c new file mode 100644 index 0000000000..5c5f7c5bef --- /dev/null +++ b/tools/xfrd/xen_domain.c @@ -0,0 +1,90 @@ +#include +#include +#include + +#ifndef _XEN_XFR_STUB_ +#include "dom0_defs.h" +#include "mem_defs.h" +#endif + +#include "xen_domain.h" +#include "marshal.h" +#include "xdr.h" + +#define MODULE_NAME "XFRD" +#define DEBUG 1 +#include "debug.h" + +/** Write domain state. + * + * At some point during this the domain is suspended, and then there's no way back. + * Even if something later goes wrong we can't restart the domain. + */ +int xen_domain_snd(Conn *xend, IOStream *io, uint32_t dom, char *vmconfig, int vmconfig_n){ + int err = 0; + char buf[1024]; + int n, k, d, buf_n; + dprintf("> dom=%d\n", dom); +#ifdef _XEN_XFR_STUB_ + err = marshal_uint32(io, dom); + if(err) goto exit; + err = marshal_string(io, vmconfig, vmconfig_n); + if(err) goto exit; + n = 32 * 1024 * 1024; + buf_n = sizeof(buf); + err = marshal_uint32(io, n); + for(k = 0; k < n; k += d){ + d = n - k; + if(d > buf_n) d = buf_n; + err = marshal_bytes(io, buf, d); + if(err) goto exit; + //dprintf("> k=%d n=%d\n", k, n); + } + + exit: +#else +#endif + dprintf("< err=%d\n", err); + return err; +} + +/** Receive domain state. + * Create a new domain and store the received state into it. + */ +int xen_domain_rcv(IOStream *io, uint32_t *dom, char **vmconfig, int *vmconfig_n){ + int err = 0; + char buf[1024]; + int n, k, d, buf_n; + dprintf(">\n"); +#ifdef _XEN_XFR_STUB_ + err = unmarshal_uint32(io, dom); + if(err) goto exit; + err = unmarshal_new_string(io, vmconfig, vmconfig_n); + if(err) goto exit; + err = unmarshal_uint32(io, &n); + buf_n = sizeof(buf); + for(k = 0; k < n; k += d){ + d = n - k; + if(d > buf_n) d = buf_n; + err = unmarshal_bytes(io, buf, d); + if(err) goto exit; + //dprintf("> k=%d n=%d\n", k, n); + } + exit: +#else +#endif + dprintf("< err=%d\n", err); + return err; +} + +/** Configure a new domain. Talk to xend. Use libcurl? + */ +int xen_domain_configure(uint32_t dom, char *vmconfig, int vmconfig_n){ + int err = 0; + dprintf(">\n"); +#ifdef _XEN_XFR_STUB_ +#else +#endif + dprintf("< err=%d\n", err); + return err; +} diff --git a/tools/xfrd/xen_domain.h b/tools/xfrd/xen_domain.h new file mode 100644 index 0000000000..4618c642b7 --- /dev/null +++ b/tools/xfrd/xen_domain.h @@ -0,0 +1,15 @@ +#ifndef _XFRD_XEN_DOMAIN_H_ +#define _XFRD_XEN_DOMAIN_H_ +#include +#include +#include "connection.h" + +/** Define to use stubs. Undefine to use Xen ops. */ +//#define _XEN_XFR_STUB_ + +extern int xen_domain_snd(Conn *xend, IOStream *io, uint32_t dom, char *vmconfig, int vmconfig_n); +extern int xen_domain_rcv(IOStream *io, uint32_t *dom, char **vmconfig, int *vmconfig_n); + + +extern int xen_domain_configure(uint32_t dom, char *vmconfig, int vmconfig_n); +#endif diff --git a/tools/xfrd/xfrd.c b/tools/xfrd/xfrd.c new file mode 100644 index 0000000000..1836231075 --- /dev/null +++ b/tools/xfrd/xfrd.c @@ -0,0 +1,1127 @@ +/** @file + * XFRD - Domain Transfer Daemon for Xen. + * + * The xfrd is forked by xend to transfer a vm to a remote system. + * + * The vm is suspended, then its state and memory are transferred to the remote system. + * The remote system attempts to create a vm and copy the transferred state and memory into it, + * finally resuming the vm. If all is OK the vm ends up running on the remote + * system and is removed from the originating system. If the transfer does not complete + * successfully the originating system attempts to resume the vm. + * The children exit when the transfer completes. + * + * @author Mike Wray + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "allocate.h" +#include "file_stream.h" +#include "string_stream.h" +#include "lzi_stream.h" +#include "sys_net.h" +#include "sys_string.h" + +#include "xdr.h" +#include "enum.h" +#include "xfrd.h" + +#include "xen_domain.h" + +#include "connection.h" +#include "select.h" + +#define MODULE_NAME "XFRD" +#define DEBUG 1 +#include "debug.h" + +/* +sender: + xend connects to xfrd and writes migrate message + xend writes domain config to xfrd + + xfrd forks + + xfrd connects to peer + xfrd sends hello, reads response + xfrd sends domain + xfrd reads response + reports progress/status to xend + + xend reads xfrd for progress/status, disconnects + If ok, destroys domain. + If not ok, unpauses domain. + +receiver: + xfrd accepts connection on inbound port + xfrd forks and accepts connection + xfrd receives hello, writes response + xfrd receives domain + xfrd connects to xend, configures new domain + xfrd writes status back to peer, child exits + + + (xfr.hello ) + (xfr.err ) + + xend->xfrd (xfr.migrate ) + (xfr.save ) + xfrd->xend (xfr.suspend ) + xfrd->xend (xfr.progress ) + xfrd->xend (xfr.err ) | (xfr.ok ) + xfrd->xfrd (xfr.xfr ) + xfrd->xfrd (xfr.err ) | (xfr.ok ) + + xfrd->xend (xfr.configure ) + */ + +Sxpr oxfr_configure; // (xfr.configure ) +Sxpr oxfr_err; // (xfr.err ) +Sxpr oxfr_hello; // (xfr.hello ) +Sxpr oxfr_migrate; // (xfr.migrate ) +Sxpr oxfr_ok; // (xfr.ok ) +Sxpr oxfr_progress; // (xfr.progress ) +Sxpr oxfr_save; // (xfr.save ) +Sxpr oxfr_suspend; // (xfr.suspend ) +Sxpr oxfr_xfr; // (xfr.xfr ) + +void xfr_init(void){ + oxfr_configure = intern("xfr.configure"); + oxfr_err = intern("xfr.err"); + oxfr_hello = intern("xfr.hello"); + oxfr_migrate = intern("xfr.migrate"); + oxfr_ok = intern("xfr.ok"); + oxfr_progress = intern("xfr.progress"); + oxfr_save = intern("xfr.save"); + oxfr_suspend = intern("xfr.suspend"); + oxfr_xfr = intern("xfr.xfr"); +} + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +#define PROGRAM "xfrd" + +#define OPT_PORT 'P' +#define KEY_PORT "port" +#define DOC_PORT "\n\txfr port (as a number or service name)" + +#define OPT_COMPRESS 'Z' +#define KEY_COMPRESS "compress" +#define DOC_COMPRESS "\n\tuse compression for migration" + +#define OPT_HELP 'h' +#define KEY_HELP "help" +#define DOC_HELP "\n\tprint help" + +#define OPT_VERSION 'v' +#define KEY_VERSION "version" +#define DOC_VERSION "\n\tprint version" + +#define OPT_VERBOSE 'V' +#define KEY_VERBOSE "verbose" +#define DOC_VERBOSE "\n\tverbose flag" + +/** Print a usage message. + * Prints to stdout if err is zero, and exits with 0. + * Prints to stderr if err is non-zero, and exits with 1. + */ +void usage(int err){ + FILE *out = (err ? stderr : stdout); + + fprintf(out, "Usage: %s [options]\n", PROGRAM); + fprintf(out, "-%c, --%s %s\n", OPT_PORT, KEY_PORT, DOC_PORT); + fprintf(out, "-%c, --%s %s\n", OPT_COMPRESS, KEY_COMPRESS, DOC_COMPRESS); + fprintf(out, "-%c, --%s %s\n", OPT_VERBOSE, KEY_VERBOSE, DOC_VERBOSE); + fprintf(out, "-%c, --%s %s\n", OPT_VERSION, KEY_VERSION, DOC_VERSION); + fprintf(out, "-%c, --%s %s\n", OPT_HELP, KEY_HELP, DOC_HELP); + exit(err ? 1 : 0); +} + +/** Short options. Options followed by ':' take an argument. */ +static char *short_opts = (char[]){ + OPT_PORT, ':', + OPT_COMPRESS, + OPT_HELP, + OPT_VERSION, + OPT_VERBOSE, + 0 }; + +/** Long options. */ +static struct option const long_opts[] = { + { KEY_PORT, required_argument, NULL, OPT_PORT }, + { KEY_COMPRESS, no_argument, NULL, OPT_COMPRESS }, + { KEY_HELP, no_argument, NULL, OPT_HELP }, + { KEY_VERSION, no_argument, NULL, OPT_VERSION }, + { KEY_VERBOSE, no_argument, NULL, OPT_VERBOSE }, + { NULL, 0, NULL, 0 } +}; + +typedef struct Args { + int bufsize; + unsigned long port; + int verbose; + int compress; +} Args; + +/** Transfer states. */ +enum { + XFR_INIT, + XFR_HELLO, + XFR_STATE, + XFR_RUN, + XFR_FAIL, + XFR_DONE, + XFR_MAX +}; + +/** Initialize an array element for a constant to its string name. */ +#define VALDEF(val) { val, #val } + +/** Names for the transfer states. */ +static EnumDef xfr_states[] = { + VALDEF(XFR_INIT), + VALDEF(XFR_HELLO), + VALDEF(XFR_STATE), + VALDEF(XFR_RUN), + VALDEF(XFR_FAIL), + VALDEF(XFR_DONE), + { 0, NULL } +}; + + +/** State machine for transfer. */ +typedef struct XfrState { + /** Current state. */ + int state; + /** Error codes for the states. */ + int state_err[XFR_MAX]; + /** First error. */ + int err; + /** State when first error happened. */ + int err_state; + + uint32_t vmid; + char* vmconfig; + int vmconfig_n; + unsigned long xfr_port; + char *xfr_host; + uint32_t vmid_new; +} XfrState; + +/** Get the name of a transfer state. + * + * @param s state + * @return name + */ +char * xfr_state_name(int s){ + return enum_val_to_name(s, xfr_states); +} + +/** Set the state of a transfer. + * + * @param s transfer + * @param state state + * @return state + */ +int XfrState_set_state(XfrState *s, int state){ + s->state = state; + return s->state; +} + +/** Get the state of a transfer. + * + * @param s transfer + * @return state + */ +int XfrState_get_state(XfrState *s){ + return s->state; +} + +/** Set an error in the current state. + * Does nothing if an error is already set. + * + * @param s transfer + * @param err error + * @return error + */ +int XfrState_set_err(XfrState *s, int err){ + if(!s->state_err[s->state]){ + s->state_err[s->state] = err; + } + if(!s->err){ + s->err = err; + s->err_state = s->state; + } + return err; +} + +/** Get the error in the current state. + * + * @param s transfer + * @return error + */ +int XfrState_get_err(XfrState *s){ + return s->state_err[s->state]; +} + +/** Get the first error of a transfer. + * + * @param s transfer + * @return error + */ +int XfrState_first_err(XfrState *s){ + return s->err; +} + +/** Get the state a transfer was in when it had its first error. + * + * @param s transfer + * @return error state + */ +int XfrState_first_err_state(XfrState *s){ + return s->err_state; +} + +/** Xfrd arguments. */ +static Args _args = {}; + +/** Xfrd arguments. */ +static Args *args = &_args; + +/** Set xfrd default arguments. + * + * @param args arguments to set + */ +void set_defaults(Args *args){ + args->compress = FALSE; + args->bufsize = 128 * 1024; + args->port = htons(XFRD_PORT); +} + +int stringof(Sxpr exp, char **s){ + int err = 0; + dprintf(">\n"); + objprint(iostdout, exp, PRINT_TYPE); IOStream_print(iostdout, "\n"); + if(ATOMP(exp)){ + *s = atom_name(exp); + } else if(STRINGP(exp)){ + *s = string_string(exp); + } else { + err = -EINVAL; + *s = NULL; + } + dprintf("< err=%d s=%s\n", err, *s); + return err; +} + +int intof(Sxpr exp, int *v){ + int err = 0; + char *s; + unsigned long l; + dprintf(">\n"); + objprint(iostdout, exp, 0); IOStream_print(iostdout, "\n"); + if(INTP(exp)){ + *v = OBJ_INT(exp); + } else { + err = stringof(exp, &s); + if(err) goto exit; + err = convert_atoul(s, &l); + *v = (int)l; + } + exit: + dprintf("< err=%d v=%d\n", err, *v); + return err; +} + +int addrof(Sxpr exp, uint32_t *v){ + char *h; + unsigned long a; + int err = 0; + dprintf(">\n"); + objprint(iostdout, exp, 0); IOStream_print(iostdout, "\n"); + err = stringof(exp, &h); + if(err) goto exit; + if(get_host_address(h, &a)){ + *v = a; + } else { + err = -EINVAL; + } + exit: + dprintf("< err=%d v=%x\n", err, *v); + return err; +} + +int portof(Sxpr exp, uint16_t *v){ + char *s; + int err = 0; + dprintf(">\n"); + objprint(iostdout, exp, 0); IOStream_print(iostdout, "\n"); + if(INTP(exp)){ + *v = get_ul(exp); + *v = htons(*v); + } else { + unsigned long p; + err = stringof(exp, &s); + if(err) goto exit; + if(get_service_port(s, &p)){ + *v = p; + } else { + err = -EINVAL; + } + } + exit: + dprintf("< err=%d v=%u\n", err, *v); + return err; +} + +static inline struct in_addr inaddr(uint32_t addr){ + return (struct in_addr){ .s_addr = addr }; +} + +time_t stats(time_t t0, uint64_t offset, uint64_t memory, float *percent, float *rate){ + time_t t1 = time(NULL); + *percent = (offset * 100.0f) / memory; + t1 = time(NULL) - t0; + *rate = (t1 ? offset/(t1 * 1024.0f) : 0.0f); + return t1; +} + +/** Notify success or error. + * + * @param conn connection + * @param errcode error code + * @return 0 on success, error code otherwise + */ +int xfr_error(Conn *conn, int errcode){ + int err = 0; + if(!conn->out) return -ENOTCONN; + err = pack_type(conn->out, T_CONS); + if(err) goto exit; + err = pack_bool(conn->out, 1); + if(err) goto exit; + err = pack_sxpr(conn->out, oxfr_err); + if(err) goto exit; + err = pack_bool(conn->out, 1); + if(err) goto exit; + err = pack_sxpr(conn->out, mkint(errcode)); + if(err) goto exit; + err = pack_bool(conn->out, 0); + exit: + return err; +} + +/** Read a response message - error or ok. + * + * @param conn connection + * @return 0 on success, error code otherwise + */ +int xfr_response(Conn *conn){ + int err; + Sxpr sxpr; + + dprintf(">\n"); + if(!conn->out) return -ENOTCONN; + err = unpack_sxpr(conn->in, &sxpr); + if(err) goto exit; + if(sxpr_elementp(sxpr, oxfr_err)){ + int errcode; + err = intof(sxpr_childN(sxpr, 0, ONONE), &errcode); + if(err) goto exit; + err = errcode; + } + exit: + dprintf("< err=%d\n", err); + return err; +} + +/** Get the initial hello message and check the protocol version. + * It is an error to receive anything other than a hello message + * with the correct protocol version. + * + * @param conn connection + * @return 0 on success, error code otherwise + */ +int xfr_hello(Conn *conn){ + int err; + uint32_t major = XFR_PROTO_MAJOR, minor = XFR_PROTO_MINOR; + uint32_t hello_major, hello_minor; + Sxpr sxpr; + if(!conn->in) return -ENOTCONN; + dprintf(">\n"); + err = unpack_sxpr(conn->in, &sxpr); + if(err) goto exit; + if(!sxpr_elementp(sxpr, oxfr_hello)){ + dprintf("> sxpr_elementp test failed\n"); + err = -EINVAL; + goto exit; + } + err = intof(sxpr_childN(sxpr, 0, ONONE), &hello_major); + if(err) goto exit; + err = intof(sxpr_childN(sxpr, 1, ONONE), &hello_minor); + if(err) goto exit; + if(hello_major != major || hello_minor != minor){ + eprintf("> Wanted protocol version %d.%d, got %d.%d", + major, minor, hello_major, hello_minor); + err = -EINVAL; + goto exit; + } + exit: + xfr_error(conn, err); + if(err){ + eprintf("> Hello failed: %d\n", err); + } + dprintf("< err=%d\n", err); + return err; +} + +/** Send the initial hello message. + * + * @param conn connection + * @param msg message + * @return 0 on success, error code otherwise + */ +int xfr_send_hello(Conn *conn){ + int err = 0; + dprintf(">\n"); + err = pack_type(conn->out, T_CONS); + if(err) goto exit; + err = pack_bool(conn->out, 1); + if(err) goto exit; + err = pack_sxpr(conn->out, oxfr_hello); + if(err) goto exit; + err = pack_bool(conn->out, 1); + if(err) goto exit; + err = pack_sxpr(conn->out, mkint(XFR_PROTO_MAJOR)); + if(err) goto exit; + err = pack_bool(conn->out, 1); + if(err) goto exit; + err = pack_sxpr(conn->out, mkint(XFR_PROTO_MINOR)); + if(err) goto exit; + err = pack_bool(conn->out, 0); + IOStream_flush(conn->out); + dprintf("> xfr_response...\n"); + err = xfr_response(conn); + exit: + dprintf("< err=%d\n", err); + return err; +} + +int xfr_send_xfr(Conn *conn, uint32_t vmid){ + int err; + err = pack_type(conn->out, T_CONS); + if(err) goto exit; + err = pack_bool(conn->out, 1); + if(err) goto exit; + err = pack_sxpr(conn->out, oxfr_xfr); + if(err) goto exit; + err = pack_bool(conn->out, 1); + if(err) goto exit; + err = pack_sxpr(conn->out, mkint(vmid)); + if(err) goto exit; + err = pack_bool(conn->out, 0); + if(err) goto exit; + exit: + return err; +} + +int xfr_send_ok(Conn *conn, uint32_t vmid){ + int err = 0; + err = pack_type(conn->out, T_CONS); + if(err) goto exit; + err = pack_bool(conn->out, 1); + if(err) goto exit; + err = pack_sxpr(conn->out, oxfr_ok); + if(err) goto exit; + err = pack_bool(conn->out, 1); + if(err) goto exit; + err = pack_sxpr(conn->out, mkint(vmid)); + if(err) goto exit; + err = pack_bool(conn->out, 0); + exit: + return err; +} + +int xfr_send_suspend(Conn *conn, uint32_t vmid){ + int err = 0; + + err = pack_type(conn->out, T_CONS); + if(err) goto exit; + err = pack_bool(conn->out, 1); + if(err) goto exit; + err = pack_sxpr(conn->out, oxfr_suspend); + if(err) goto exit; + err = pack_bool(conn->out, 1); + if(err) goto exit; + err = pack_sxpr(conn->out, mkint(vmid)); + if(err) goto exit; + err = pack_bool(conn->out, 0); + exit: + return err; +} + +/** Get vm state. Send transfer message. + * + * @param peer connection + * @param msg message + * @return 0 on success, error code otherwise + */ +int xfr_send_state(XfrState *state, Conn *xend, Conn *peer){ + int err = 0; + Sxpr sxpr; + + dprintf(">\n"); + XfrState_set_state(state, XFR_STATE); + // Send xfr message and the domain state. + err = xfr_send_xfr(peer, state->vmid); + if(err) goto exit; + err = xen_domain_snd(xend, peer->out, state->vmid, state->vmconfig, state->vmconfig_n); + if(err) goto exit; + IOStream_flush(peer->out); + // Read the response from the peer. + err = unpack_sxpr(peer->in, &sxpr); + if(err) goto exit; + if(sxpr_elementp(sxpr, oxfr_err)){ + // Error. + int errcode; + err = intof(sxpr_childN(sxpr, 0, ONONE), &errcode); + if(!err) err = errcode; + } else if(sxpr_elementp(sxpr, oxfr_ok)){ + // Ok - get the new domain id. + err = intof(sxpr_childN(sxpr, 0, ONONE), &state->vmid_new); + xfr_error(peer, err); + } else { + // Anything else is invalid. But it may be too late. + err = -EINVAL; + xfr_error(peer, err); + } + exit: + XfrState_set_err(state, err); + dprintf("< err=%d\n", err); + return err; +} + +/** Finish the transfer. + */ +int xfr_send_done(XfrState *state, Conn *xend){ + int err = 0; + int first_err = 0; + + first_err = XfrState_first_err(state); + if(first_err){ + XfrState_set_state(state, XFR_FAIL); + } else { + XfrState_set_state(state, XFR_DONE); + } + if(first_err){ + err = xfr_error(xend, first_err); + } else { + // Report new domain id to xend. + err = xfr_send_ok(xend, state->vmid_new); + } + + XfrState_set_err(state, err); + if(XfrState_first_err(state)){ + int s, serr; + + wprintf("> Transfer errors:\n"); + for(s = 0; s < XFR_MAX; s++){ + serr = state->state_err[s]; + if(!serr) continue; + wprintf("> state=%-12s err=%d\n", xfr_state_name(s), serr); + } + } else { + wprintf("> Transfer OK\n"); + } + dprintf("< err=%d\n", err); + return err; +} + +/** Migrate a vm to another node. + * + * @param xend connection + * @return 0 on success, error code otherwise + */ +int xfr_send(Args *args, XfrState *state, Conn *xend, uint32_t addr, uint32_t port){ + int err = 0; + Conn _peer = {}, *peer = &_peer; + int flags = 0; + struct in_addr xfr_addr; + uint16_t xfr_port; + time_t t0 = time(NULL), t1; + + dprintf(">\n"); + flags |= CONN_NOBUFFER; + if(args->compress){ + flags |= CONN_WRITE_COMPRESS; + } + xfr_addr.s_addr = addr; + xfr_port = port; + if(!xfr_port) xfr_port = htons(XFRD_PORT); + dprintf("> Xfr vmid=%u\n", state->vmid); + dprintf("> Xfr xfr_addr=%s:%d\n", inet_ntoa(xfr_addr), ntohs(xfr_port)); + err = Conn_connect(peer, flags, xfr_addr, xfr_port); + if(err) goto exit; + printf("\n"); + XfrState_set_state(state, XFR_HELLO); + // Send hello message. + err = xfr_send_hello(peer); + if(err) goto exit; + printf("\n"); + // Send vm state. + err = xfr_send_state(state, xend, peer); + if(err) goto exit; + if(args->compress){ + IOStream *zio = peer->out; + int plain_bytes = lzi_stream_plain_bytes(zio); + int comp_bytes = lzi_stream_comp_bytes(zio); + float ratio = lzi_stream_ratio(zio); + dprintf("> Compression: plain %d bytes, compressed %d bytes, ratio %3.2f\n", + plain_bytes, comp_bytes, ratio); + } + printf("\n"); + exit: + dprintf("> err=%d\n", err); + if(err && !XfrState_get_err(state)){ + XfrState_set_err(state, err); + } + Conn_close(peer); + if(!err){ + t1 = time(NULL) - t0; + dprintf("> Transfer complete in %lu seconds\n", t1); + } + dprintf("> done err=%d, notifying xend...\n", err); + xfr_send_done(state, xend); + dprintf("< err=%d\n", err); + return err; +} + +/** Save a vm to file. + */ +int xfr_save(Args *args, XfrState *state, Conn *xend, char *file){ + int err = 0; + IOStream *io = NULL; + + io = file_stream_fopen(file, "wb"); + if(!io){ + err = -EIO; + goto exit; + } + err = xen_domain_snd(xend, io, state->vmid, state->vmconfig, state->vmconfig_n); + exit: + if(io){ + IOStream_close(io); + IOStream_free(io); + } + return err; +} + +/** Accept the transfer of a vm from another node. + * + * @param peer connection + * @param msg message + * @return 0 on success, error code otherwise + */ +int xfr_recv(Args *args, XfrState *state, Conn *peer){ + int err = 0; + time_t t0 = time(NULL), t1; + + dprintf(">\n"); + err = xen_domain_rcv(peer->in, &state->vmid_new, &state->vmconfig, &state->vmconfig_n); + if(err) goto exit; + + err = xen_domain_configure(state->vmid_new, state->vmconfig, state->vmconfig_n); + if(err) goto exit; + + // Report new domain id to peer. + err = xfr_send_ok(peer, state->vmid_new); + if(err) goto exit; + // Get the final ok. + err = xfr_response(peer); + exit: + if(!err){ + t1 = time(NULL) - t0; + dprintf("> Transfer complete in %lu seconds\n", t1); + } + if(err){ + xfr_error(peer, err); + } + dprintf("< err=%d\n", err); + return err; +} + +/** Listen for a hello followed by a service request. + * The request can be from the local xend or from xfrd on another node. + * + * @param peersock socket + * @param peer_in peer address + * @return 0 on success, error code otherwise + */ +int xfrd_service(Args *args, int peersock, struct sockaddr_in peer_in){ + int err = 0; + Sxpr sxpr; + Conn _conn = {}, *conn = &_conn; + int flags = CONN_NOBUFFER; + + dprintf(">\n"); + err = Conn_init(conn, flags, peersock, peer_in); + if(err) goto exit; + err = xfr_hello(conn); + if(err) goto exit; + err = unpack_sxpr(conn->in, &sxpr); + if(err) goto exit; + if(sxpr_elementp(sxpr, oxfr_migrate)){ + // Migrate message from xend. + uint32_t addr; + uint16_t port; + XfrState _state = {}, *state = &_state; + int n = 0; + + dprintf("> xfr.migrate\n"); + err = intof(sxpr_childN(sxpr, n++, ONONE), &state->vmid); + if(err) goto exit; + err = stringof(sxpr_childN(sxpr, n++, ONONE), &state->vmconfig); + if(err) goto exit; + state->vmconfig_n = strlen(state->vmconfig); + err = addrof(sxpr_childN(sxpr, n++, ONONE), &addr); + if(err) goto exit; + err = portof(sxpr_childN(sxpr, n++, ONONE), &port); + if(err) goto exit; + err = xfr_send(args, state, conn, addr, port); + + } else if(sxpr_elementp(sxpr, oxfr_save)){ + // Save message from xend. + char *file; + XfrState _state = {}, *state = &_state; + int n = 0; + + dprintf("> xfr.save\n"); + err = intof(sxpr_childN(sxpr, n++, ONONE), &state->vmid); + if(err) goto exit; + err = stringof(sxpr_childN(sxpr, n++, ONONE), &state->vmconfig); + if(err) goto exit; + state->vmconfig_n = strlen(state->vmconfig); + err = stringof(sxpr_childN(sxpr, n++, ONONE), &file); + if(err) goto exit; + err = xfr_save(args, state, conn, file); + + } else if(sxpr_elementp(sxpr, oxfr_xfr)){ + // Xfr message from peer xfrd. + XfrState _state = {}, *state = &_state; + int n = 0; + + dprintf("> xfr.xfr\n"); + err = intof(sxpr_childN(sxpr, n++, ONONE), &state->vmid); + if(err) goto exit; + err = xfr_recv(args, state, conn); + + } else{ + // Anything else is invalid. + err = -EINVAL; + dprintf("> Invalid message: "); + objprint(iostderr, sxpr, 0); + IOStream_print(iostderr, "\n"); + xfr_error(conn, err); + } + exit: + Conn_close(conn); + dprintf("< err=%d\n", err); + return err; +} + +/** Accept an incoming connection. + * + * @param sock tcp socket + * @return 0 on success, error code otherwise + */ +int xfrd_accept(Args *args, int sock){ + struct sockaddr_in peer_in; + struct sockaddr *peer = (struct sockaddr *)&peer_in; + socklen_t peer_n = sizeof(peer_in); + int peersock; + pid_t pid; + int err = 0; + + dprintf(">\n"); + dprintf("> accept...\n"); + peersock = accept(sock, peer, &peer_n); + dprintf("> accept=%d\n", peersock); + if(peersock < 0){ + perror("accept"); + err = -errno; + goto exit; + } + iprintf("> Accepted connection from %s:%d\n", + inet_ntoa(peer_in.sin_addr), htons(peer_in.sin_port)); + pid = fork(); + if(pid > 0){ + // Parent, fork succeeded. + iprintf("> Forked child pid=%d\n", pid); + close(peersock); + } else if (pid < 0){ + // Parent, fork failed. + perror("fork"); + close(peersock); + } else { + // Child. + iprintf("> Xfr service for %s:%d\n", + inet_ntoa(peer_in.sin_addr), htons(peer_in.sin_port)); + err = xfrd_service(args, peersock, peer_in); + iprintf("> Xfr service err=%d\n", err); + shutdown(peersock, 2); + exit(err ? 1 : 0); + } + exit: + dprintf("< err=%d\n", err); + return err; +} + +/** Socket select loop. + * Accepts connections on the tcp socket. + * + * @param listen_sock tcp listen socket + * @return 0 on success, error code otherwise + */ +int xfrd_select(Args *args, int listen_sock){ + int err = 0; + SelectSet set = {}; + dprintf("> socks: %d\n", listen_sock); + while(1){ + SelectSet_zero(&set); + SelectSet_add_read(&set, listen_sock); + err = SelectSet_select(&set, NULL); + if(err < 0){ + if(errno == EINTR) continue; + perror("select"); + goto exit; + } + if(FD_ISSET(listen_sock, &set.rd)){ + xfrd_accept(args, listen_sock); + } + } + exit: + dprintf("< err=%d\n", err); + return err; +} + +/** Create a socket. + * + * @param args program arguments + * @param socktype socket type + * @param reuse whether to set SO_REUSEADDR + * @param val return value for the socket + * @return 0 on success, error code otherwise + */ +int create_socket(Args *args, int socktype, int reuse, int *val){ + int err = 0; + int sock = 0; + struct sockaddr_in addr_in; + struct sockaddr *addr = (struct sockaddr *)&addr_in; + socklen_t addr_n = sizeof(addr_in); + + dprintf(">\n"); + // Create socket and bind it. + sock = socket(AF_INET, socktype, 0); + if(sock < 0){ + err = -errno; + goto exit; + } + addr_in.sin_family = AF_INET; + addr_in.sin_addr.s_addr = INADDR_ANY; + addr_in.sin_port = args->port; + dprintf("> port=%d\n", ntohs(addr_in.sin_port)); + if(reuse){ + // Set socket option to reuse address. + int val = 1; + err = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)); + if(err < 0){ + err = -errno; + perror("setsockopt"); + goto exit; + } + } + err = bind(sock, addr, addr_n); + if(err < 0){ + err = -errno; + perror("bind"); + goto exit; + } + exit: + *val = (err ? -1 : sock); + dprintf("< err=%d\n", err); + return err; +} + +/** Create the tcp listen socket. + * + * @param args program arguments + * @param val return value for the socket + * @return 0 on success, error code otherwise + */ +int xfrd_listen_socket(Args *args, int *val){ + int err = 0; + int sock; + dprintf(">\n"); + err = create_socket(args, SOCK_STREAM, 1, &sock); + if(err) goto exit; + dprintf("> listen...\n"); + err = listen(sock, 5); + if(err < 0){ + err = -errno; + perror("listen"); + goto exit; + } + exit: + *val = (err ? -1 : sock); + if(err) close(sock); + dprintf("< err=%d\n", err); + return err; +} + +/** Type for signal handling functions. */ +typedef void SignalAction(int code, siginfo_t *info, void *data); + +/** Handle SIGCHLD by getting child exit status. + * This prevents child processes being defunct. + * + * @param code signal code + * @param info signal info + * @param data + */ +void sigaction_SIGCHLD(int code, siginfo_t *info, void *data){ + int status; + pid_t pid; + //dprintf("> child_exit=%d waiting...\n", child_exit); + pid = wait(&status); + dprintf("> child pid=%d status=%d\n", pid, status); +} + +/** Handle SIGPIPE. + * + * @param code signal code + * @param info signal info + * @param data + */ +void sigaction_SIGPIPE(int code, siginfo_t *info, void *data){ + dprintf("> SIGPIPE\n"); + //fflush(stdout); + //fflush(stderr); + //exit(1); +} + +/** Handle SIGALRM. + * + * @param code signal code + * @param info signal info + * @param data + */ +void sigaction_SIGALRM(int code, siginfo_t *info, void *data){ + dprintf("> SIGALRM\n"); +} + +/** Install a handler for a signal. + * + * @param signum signal + * @param action handler + * @return 0 on success, error code otherwise + */ +int catch_signal(int signum, SignalAction *action){ + int err = 0; + struct sigaction sig = {}; + sig.sa_sigaction = action; + sig.sa_flags = SA_SIGINFO; + err = sigaction(signum, &sig, NULL); + if(err){ + perror("sigaction"); + } + return err; +} + +/** Transfer daemon main program. + * + * @param args program arguments + * @return 0 on success, error code otherwise + */ +int xfrd_main(Args *args){ + int err = 0; + int listen_sock; + + dprintf(">\n"); + catch_signal(SIGCHLD,sigaction_SIGCHLD); + catch_signal(SIGPIPE,sigaction_SIGPIPE); + catch_signal(SIGALRM,sigaction_SIGALRM); + err = xfrd_listen_socket(args, &listen_sock); + if(err) goto exit; + err = xfrd_select(args, listen_sock); + exit: + close(listen_sock); + dprintf("< err=%d\n", err); + return err; +} + +/** Parse command-line arguments and call the xfrd main program. + * + * @param arg argument count + * @param argv arguments + * @return 0 on success, 1 otherwise + */ +int main(int argc, char *argv[]){ + int err = 0; + int key = 0; + int long_index = 0; + + set_defaults(args); + while(1){ + key = getopt_long(argc, argv, short_opts, long_opts, &long_index); + if(key == -1) break; + switch(key){ + case OPT_PORT: + err = !convert_service_to_port(optarg, &args->port); + if(err) goto exit; + break; + case OPT_COMPRESS: + args->compress = TRUE; + break; + case OPT_HELP: + usage(0); + break; + case OPT_VERBOSE: + args->verbose = TRUE; + break; + case OPT_VERSION: + printf("> Version %d.%d\n", XFR_PROTO_MAJOR, XFR_PROTO_MINOR); + exit(0); + break; + default: + usage(EINVAL); + break; + } + } + xfr_init(); + err = xfrd_main(args); + exit: + if(err && key > 0){ + fprintf(stderr, "Error in arg %c\n", key); + } + return (err ? 1 : 0); +} + diff --git a/tools/xfrd/xfrd.h b/tools/xfrd/xfrd.h new file mode 100644 index 0000000000..2537fd67ab --- /dev/null +++ b/tools/xfrd/xfrd.h @@ -0,0 +1,14 @@ +#ifndef _XFRD_XFRD_H_ +#define _XFRD_XFRD_H_ + +/** Xend port in host order. */ +#define XEND_PORT 8001 + +/** Xfrd port in host order. */ +#define XFRD_PORT 8002 + +/** Protocol version. */ +#define XFR_PROTO_MAJOR 1 +#define XFR_PROTO_MINOR 0 + +#endif diff --git a/tools/xfrd/xfrdClient.py b/tools/xfrd/xfrdClient.py new file mode 100755 index 0000000000..b8a3554729 --- /dev/null +++ b/tools/xfrd/xfrdClient.py @@ -0,0 +1,127 @@ +#!/bin/env python +""" +Test client for the migration daemon (xfrd). + +Author: Mike Wray + +""" +import getopt +import sys +import os +from socket import * +import StringIO + +sys.path.append("/home/mjw/repos-bk/xeno-unstable.bk/tools/python") + +import xen.xend.sxp as sxp +from xen.xend.packing import SxpPacker, SxpUnpacker + +XFRD_PORT = 8002 + +verbose = 0 + +class TCPClient: + + def __init__(self, host, port): + print ">TCPClient" + self.sock = socket(AF_INET, SOCK_STREAM, 0) + print ">TCPClient sock=", self.sock + print ">TCPClient> connect ", host, port + v = self.sock.connect((host, port)) + print ">TCPClient> connect=", v + # Send plain header (no gzip). + #self.sock.send("\0\0") + + self.sockin = self.sock.makefile("r") + self.sockout = self.sock.makefile("w") + self.packer = SxpPacker(self.sockout) + self.unpacker = SxpUnpacker(self.sockin) + #pass + + def request(self, req): + print "request>", req + self.packer.pack(req) + self.sockout.flush() + print "request<" + + def request_hello(self): + self.request(['xfr.hello', XFR_PROTO_MAJOR, XFR_PROTO_MINOR]) + + def request_migrate(self, vmid, vhost, vport, vmconfig='(vm)'): + self.request(['xfr.migrate', vmid, vmconfig, vhost, vport]) + + def read(self): + while(1): + v = self.unpacker.unpack() + print 'read>', v + if v[0] == 'xfr.err' and v[1]: return + if v[0] == 'xfr.ok': return + +XFR_PROTO_MAJOR = 1 +XFR_PROTO_MINOR = 0 + +host_default = "localhost" +port_default = XFRD_PORT +vhost_default = "localhost" +vport_default = 8003 +vmid_default = 1 + +# Short options. Options followed by ':' need a parameter. +short_opts = 'h' + +# Long options. Options ending in '=' need a parameter. +long_opts = [ 'host=', 'port=', 'vhost=', 'vport=', 'vmid=', 'verbose', 'help'] + +def usage(err=None): + if err: + out = sys.stderr + else: + out = sys.stdout + print >> out, 'Usage: %s [options] [command...]\n' % sys.argv[0] + print >> out, '--host \n\tHost to initiate transfer on. Default %s.' % host_default + print >> out, '--port \n\tPort to initiate transfer on. Default %d.' % port_default + print >> out, '--vhost \n\tHost to transfer VM to. Default %s.' % vhost_default + print >> out, '--vport \n\tPort to transfer VM to. Default %d.' % vport_default + print >> out, '--vmid \n\tVM id. Default %d.' % vmid_default + print >> out, '--help\n\tPrint help.' + +def main(argv): + global verbose + host = host_default + port = port_default + vhost = vhost_default + vport = vport_default + vmid = vmid_default + + try: + opts, args = getopt.getopt(argv[1:], short_opts, long_opts) + except getopt.GetoptError, ex: + print >>sys.stderr, 'Error:', ex + usage(1) + sys.exit(1) + + for key, val in opts: + if key == '--help': + usage() + sys.exit(0) + elif key == '--host': + host = val + elif key == '--port': + port = int(val) + elif key == '--vhost': + vhost = val + elif key == '--vport': + vport = int(val) + elif key == '--vmid': + vmid = int(val) + + print "host=%s port=%d" % (host, port) + print "vhost=%s vport=%d vmid=%d" % (vhost, vport, vmid) + client = TCPClient(gethostbyname(host), port) + client.request_hello() + client.request_migrate(vmid, gethostbyname(vhost), vport) + client.read() + +if __name__ == '__main__': + main(sys.argv) + diff --git a/xen/arch/x86/Makefile b/xen/arch/x86/Makefile index f6ef36afce..039df504ef 100644 --- a/xen/arch/x86/Makefile +++ b/xen/arch/x86/Makefile @@ -6,6 +6,9 @@ OBJS := $(subst pdb-linux.o,,$(OBJS)) OBJS := $(subst pdb-stub.o,,$(OBJS)) endif +OBJS += $(patsubst %.S,%.o,$(wildcard $(TARGET_SUBARCH)/*.S)) +OBJS += $(patsubst %.c,%.o,$(wildcard $(TARGET_SUBARCH)/*.c)) + LINK_BASE := 0xFC500000 # Xen is linked here LOAD_BASE := 0x00100000 # Xen is loaded here @@ -23,3 +26,5 @@ default: boot/$(TARGET_SUBARCH).o $(OBJS) clean: rm -f *.o *~ core boot/*.o boot/*~ boot/core + rm -f $(TARGET_SUBARCH)/*.o $(TARGET_SUBARCH)/*~ + rm -f $(TARGETSUBARCH)/core diff --git a/xen/arch/x86/Rules.mk b/xen/arch/x86/Rules.mk index 5963e99d80..560b381845 100644 --- a/xen/arch/x86/Rules.mk +++ b/xen/arch/x86/Rules.mk @@ -8,7 +8,7 @@ CFLAGS := -nostdinc -fno-builtin -fno-common -fno-strict-aliasing -O3 CFLAGS += -iwithprefix include -Wall -Werror -fomit-frame-pointer CFLAGS += -I$(BASEDIR)/include -Wno-pointer-arith -Wredundant-decls -LDFLAGS := -T xen.lds -N +LDFLAGS := -T $(TARGET_SUBARCH)/xen.lds -N ifeq ($(TARGET_SUBARCH),x86_32) CFLAGS += -m32 -march=i686 diff --git a/xen/arch/x86/apic.c b/xen/arch/x86/apic.c index cd1733c173..64974f670d 100644 --- a/xen/arch/x86/apic.c +++ b/xen/arch/x86/apic.c @@ -491,9 +491,9 @@ void __init init_apic_mappings(void) *****************************************************************************/ /* used for system time scaling */ -static unsigned int bus_freq; -static u32 bus_cycle; /* length of one bus cycle in pico-seconds */ -static u32 bus_scale; /* scaling factor convert ns to bus cycles */ +static unsigned long bus_freq; /* KAF: pointer-size avoids compile warns. */ +static u32 bus_cycle; /* length of one bus cycle in pico-seconds */ +static u32 bus_scale; /* scaling factor convert ns to bus cycles */ /* * The timer chip is already set up at HZ interrupts per second here, diff --git a/xen/arch/x86/boot/x86_32.S b/xen/arch/x86/boot/x86_32.S index ae1d72ad23..fea54c1336 100644 --- a/xen/arch/x86/boot/x86_32.S +++ b/xen/arch/x86/boot/x86_32.S @@ -19,8 +19,25 @@ ENTRY(start) /* Checksum: must be the negated sum of the first two fields. */ .long -0x1BADB004 +bad_cpu_msg: + .asciz "Bad CPU: we need at least a P6-compatible core." bad_cpu: - jmp bad_cpu + mov $SYMBOL_NAME(bad_cpu_msg)-__PAGE_OFFSET,%esi + mov $0xB8000,%edi # VGA framebuffer +1: mov (%esi),%bl + test %bl,%bl # Terminate on '\0' sentinel +2: je 2b + mov $0x3f8+5,%dx # UART Line Status Register +3: in %dx,%al + test $0x20,%al # Test THR Empty flag + je 3b + mov $0x3f8+0,%dx # UART Transmit Holding Register + mov %bl,%al + out %al,%dx # Send a character over the serial line + movsb # Write a character to the VGA framebuffer + mov $7,%al + stosb # Write an attribute to the VGA framebuffer + jmp 1b __start: /* Set up a few descriptors: on entry only CS is guaranteed good. */ diff --git a/xen/arch/x86/boot/x86_64.S b/xen/arch/x86/boot/x86_64.S index b489734ded..46af44607c 100644 --- a/xen/arch/x86/boot/x86_64.S +++ b/xen/arch/x86/boot/x86_64.S @@ -20,20 +20,27 @@ ENTRY(start) /* Checksum: must be the negated sum of the first two fields. */ .long -0x1BADB004 +bad_cpu_msg: + .asciz "Bad CPU: does not support 64-bit (long) mode." bad_cpu: - jmp bad_cpu + mov $SYMBOL_NAME(bad_cpu_msg)-__PAGE_OFFSET,%esi + mov $0xB8000,%edi # VGA framebuffer +1: mov (%esi),%bl + test %bl,%bl # Terminate on '\0' sentinel +2: je 2b + mov $0x3f8+5,%dx # UART Line Status Register +3: in %dx,%al + test $0x20,%al # Test THR Empty flag + je 3b + mov $0x3f8+0,%dx # UART Transmit Holding Register + mov %bl,%al + out %al,%dx # Send a character over the serial line + movsb # Write a character to the VGA framebuffer + mov $7,%al + stosb # Write an attribute to the VGA framebuffer + jmp 1b __start: - /* We begin by interrogating the CPU for the presence of long mode. */ - mov $0x80000000,%eax - cpuid - cmp $0x80000000,%eax # any function > 0x80000000? - jbe bad_cpu - mov $0x80000001,%eax - cpuid - bt $29,%edx # Long mode feature? - jnc bad_cpu - /* Set up a few descriptors: on entry only CS is guaranteed good. */ lgdt %cs:nopaging_gdt_descr-__PAGE_OFFSET mov $(__HYPERVISOR_DS),%ecx @@ -48,6 +55,16 @@ __start: pushl $0 popf + /* We begin by interrogating the CPU for the presence of long mode. */ + mov $0x80000000,%eax + cpuid + cmp $0x80000000,%eax # any function > 0x80000000? + jbe bad_cpu + mov $0x80000001,%eax + cpuid + bt $29,%edx # Long mode feature? + jnc bad_cpu + /* Set up FPU. */ fninit @@ -82,7 +99,7 @@ __start: /* Initialise IDT with simple error defaults. */ lea ignore_int,%edx - mov $(__HYPERVISOR_CS << 16),%eax + mov $(__HYPERVISOR_CS64 << 16),%eax mov %dx,%ax /* selector = 0x0010 = cs */ mov $0x8E00,%dx /* interrupt gate - dpl=0, present */ lea SYMBOL_NAME(idt_table)-__PAGE_OFFSET,%edi @@ -98,16 +115,19 @@ start_paging: mov $0x80050033,%eax /* hi-to-lo: PG,AM,WP,NE,ET,MP,PE */ mov %eax,%cr0 jmp 1f +1: /* Now in compatibility mode. Long-jump into 64-bit mode. */ + ljmp $(__HYPERVISOR_CS64),$1f + + .code64 1: /* Install relocated selectors (FS/GS unused). */ lgdt gdt_descr mov $(__HYPERVISOR_DS),%ecx mov %ecx,%ds mov %ecx,%es mov %ecx,%ss - ljmp $(__HYPERVISOR_CS),$1f 1: /* Paging enabled, so we can now enable GLOBAL mappings in CR4. */ - mov mmu_cr4_features,%ecx - mov %ecx,%cr4 + mov mmu_cr4_features,%rcx + mov %rcx,%cr4 /* Relocate ESP */ add $__PAGE_OFFSET,%esp @@ -123,16 +143,18 @@ start_paging: /* This is the default interrupt handler. */ int_msg: .asciz "Unknown interrupt\n" - ALIGN ignore_int: cld mov $(__HYPERVISOR_DS),%eax mov %eax,%ds mov %eax,%es - pushl $int_msg + pushq $int_msg call SYMBOL_NAME(printf) 1: jmp 1b + .code32 + .align 8 + /*** STACK LOCATION ***/ ENTRY(stack_start) @@ -144,8 +166,6 @@ ENTRY(stack_start) .globl SYMBOL_NAME(idt) .globl SYMBOL_NAME(gdt) - ALIGN - .word 0 idt_descr: .word 256*8-1 diff --git a/xen/arch/x86/process.c b/xen/arch/x86/domain.c similarity index 98% rename from xen/arch/x86/process.c rename to xen/arch/x86/domain.c index d3bf5ab4b8..459a85b393 100644 --- a/xen/arch/x86/process.c +++ b/xen/arch/x86/domain.c @@ -1,17 +1,10 @@ /* - * linux/arch/i386/kernel/process.c - * * Copyright (C) 1995 Linus Torvalds * * Pentium III FXSR, SSE support * Gareth Hughes , May 2000 */ -/* - * This file handles the architecture-dependent parts of process handling.. - */ - -#define __KERNEL_SYSCALLS__ #include #include #include @@ -192,6 +185,8 @@ void machine_power_off(void) machine_restart(0); } +#if defined(__i386__) + void new_thread(struct domain *p, unsigned long start_pc, unsigned long start_stack, @@ -361,3 +356,5 @@ long do_iopl(domid_t domain, unsigned int new_io_pl) ec->eflags = (ec->eflags & 0xffffcfff) | ((new_io_pl&3) << 12); return 0; } + +#endif diff --git a/xen/arch/x86/irq.c b/xen/arch/x86/irq.c index 950138849b..af0b0de1fb 100644 --- a/xen/arch/x86/irq.c +++ b/xen/arch/x86/irq.c @@ -89,7 +89,11 @@ void enable_irq(unsigned int irq) asmlinkage void do_IRQ(struct pt_regs regs) { +#if defined(__i386__) unsigned int irq = regs.orig_eax; +#else + unsigned int irq = 0; /* XXX */ +#endif irq_desc_t *desc = &irq_desc[irq]; struct irqaction *action; diff --git a/xen/arch/x86/mpparse.c b/xen/arch/x86/mpparse.c index ae1e1da93b..d10e4ce9dd 100644 --- a/xen/arch/x86/mpparse.c +++ b/xen/arch/x86/mpparse.c @@ -458,7 +458,7 @@ static int __init smp_read_mpc(struct mp_config_table *mpc) if ((clustered_apic_mode == CLUSTERED_APIC_NUMAQ) && mpc->mpc_oemptr) { /* We need to process the oem mpc tables to tell us which quad things are in ... */ mpc_record = 0; - smp_read_mpc_oem((struct mp_config_oemtable *) mpc->mpc_oemptr, mpc->mpc_oemsize); + smp_read_mpc_oem((struct mp_config_oemtable *)(unsigned long)mpc->mpc_oemptr, mpc->mpc_oemsize); mpc_record = 0; } @@ -824,7 +824,7 @@ void __init get_smp_config (void) * Read the physical hardware table. Anything here will * override the defaults. */ - if (!smp_read_mpc((void *)mpf->mpf_physptr)) { + if (!smp_read_mpc((void *)(unsigned long)mpf->mpf_physptr)) { smp_found_config = 0; printk(KERN_ERR "BIOS bug, MP table errors detected!...\n"); printk(KERN_ERR "... disabling SMP support. (tell your hw vendor)\n"); diff --git a/xen/arch/x86/rwlock.c b/xen/arch/x86/rwlock.c index 2ef7af16b1..d8c1b7dde5 100644 --- a/xen/arch/x86/rwlock.c +++ b/xen/arch/x86/rwlock.c @@ -6,22 +6,22 @@ asm( ".align 4\n" ".globl __write_lock_failed\n" "__write_lock_failed:\n" -" " LOCK "addl $" RW_LOCK_BIAS_STR ",(%eax)\n" +" " LOCK "addl $" RW_LOCK_BIAS_STR ",(%"__OP"ax)\n" "1: rep; nop\n" -" cmpl $" RW_LOCK_BIAS_STR ",(%eax)\n" +" cmpl $" RW_LOCK_BIAS_STR ",(%"__OP"ax)\n" " jne 1b\n" -" " LOCK "subl $" RW_LOCK_BIAS_STR ",(%eax)\n" +" " LOCK "subl $" RW_LOCK_BIAS_STR ",(%"__OP"ax)\n" " jnz __write_lock_failed\n" " ret\n" ".align 4\n" ".globl __read_lock_failed\n" "__read_lock_failed:\n" -" lock ; incl (%eax)\n" +" lock ; incl (%"__OP"ax)\n" "1: rep; nop\n" -" cmpl $1,(%eax)\n" +" cmpl $1,(%"__OP"ax)\n" " js 1b\n" -" lock ; decl (%eax)\n" +" lock ; decl (%"__OP"ax)\n" " js __read_lock_failed\n" " ret\n" ); diff --git a/xen/arch/x86/setup.c b/xen/arch/x86/setup.c index 76881db1e5..831a27e092 100644 --- a/xen/arch/x86/setup.c +++ b/xen/arch/x86/setup.c @@ -28,7 +28,11 @@ extern int do_timer_lists_from_pit; char ignore_irq13; /* set if exception 16 works */ struct cpuinfo_x86 boot_cpu_data = { 0, 0, 0, 0, -1 }; +#if defined(__x86_64__) +unsigned long mmu_cr4_features = X86_CR4_PSE | X86_CR4_PGE | X86_CR4_PAE; +#else unsigned long mmu_cr4_features = X86_CR4_PSE | X86_CR4_PGE; +#endif EXPORT_SYMBOL(mmu_cr4_features); unsigned long wait_init_idle; @@ -50,6 +54,8 @@ int acpi_force __initdata = 0; int phys_proc_id[NR_CPUS]; int logical_proc_id[NR_CPUS]; +#if defined(__i386__) + /* Standard macro to see if a specific flag is changeable */ static inline int flag_is_changeable_p(u32 flag) { @@ -77,6 +83,12 @@ static int __init have_cpuid_p(void) return flag_is_changeable_p(X86_EFLAGS_ID); } +#elif defined(__x86_64__) + +#define have_cpuid_p() (1) + +#endif + void __init get_cpu_vendor(struct cpuinfo_x86 *c) { char *v = c->x86_vendor_id; @@ -255,6 +267,7 @@ void __init identify_cpu(struct cpuinfo_x86 *c) unsigned long cpu_initialized; void __init cpu_init(void) { +#if defined(__i386__) /* XXX */ int nr = smp_processor_id(); struct tss_struct * t = &init_tss[nr]; @@ -293,6 +306,7 @@ void __init cpu_init(void) write_ptbase(¤t->mm); init_idle_task(); +#endif } static void __init do_initcalls(void) diff --git a/xen/arch/x86/smpboot.c b/xen/arch/x86/smpboot.c index 6a643ada24..ef7a39df89 100644 --- a/xen/arch/x86/smpboot.c +++ b/xen/arch/x86/smpboot.c @@ -659,7 +659,9 @@ static void __init do_boot_cpu (int apicid) map_cpu_to_boot_apicid(cpu, apicid); +#if defined(__i386__) SET_DEFAULT_FAST_TRAP(&idle->thread); +#endif idle_task[cpu] = idle; diff --git a/xen/arch/x86/time.c b/xen/arch/x86/time.c index dec7618ed7..dfe20e415d 100644 --- a/xen/arch/x86/time.c +++ b/xen/arch/x86/time.c @@ -107,7 +107,8 @@ static struct irqaction irq0 = { timer_interrupt, "timer", NULL}; static unsigned long __init calibrate_tsc(void) { - unsigned long startlow, starthigh, endlow, endhigh, count; + u64 start, end, diff; + unsigned long count; /* Set the Gate high, disable speaker */ outb((inb(0x61) & ~0x02) | 0x01, 0x61); @@ -123,22 +124,24 @@ static unsigned long __init calibrate_tsc(void) outb(CALIBRATE_LATCH & 0xff, 0x42); /* LSB of count */ outb(CALIBRATE_LATCH >> 8, 0x42); /* MSB of count */ - rdtsc(startlow, starthigh); + rdtscll(start); for ( count = 0; (inb(0x61) & 0x20) == 0; count++ ) continue; - rdtsc(endlow, endhigh); + rdtscll(end); /* Error if the CTC doesn't behave itself. */ if ( count == 0 ) return 0; - /* [endhigh:endlow] = [endhigh:endlow] - [starthigh:startlow] */ - __asm__( "subl %2,%0 ; sbbl %3,%1" - : "=a" (endlow), "=d" (endhigh) - : "g" (startlow), "g" (starthigh), "0" (endlow), "1" (endhigh) ); + diff = end - start; +#if defined(_i386__) /* If quotient doesn't fit in 32 bits then we return error (zero). */ - return endhigh ? 0 : endlow; + if ( diff & ~0xffffffffULL ) + return 0; +#endif + + return (unsigned long)diff; } diff --git a/xen/arch/x86/trampoline.S b/xen/arch/x86/trampoline.S index 064159046d..02018f347a 100644 --- a/xen/arch/x86/trampoline.S +++ b/xen/arch/x86/trampoline.S @@ -43,7 +43,11 @@ r_base = . lmsw %ax # into protected mode jmp flush_instr flush_instr: - ljmpl $__HYPERVISOR_CS, $0x100000 # 1MB +#if defined(__x86_64__) + ljmpl $__HYPERVISOR_CS32, $0x100000 # 1MB +#else + ljmpl $__HYPERVISOR_CS, $0x100000 # 1MB +#endif idt_48: .word 0 # idt limit = 0 diff --git a/xen/arch/x86/traps.c b/xen/arch/x86/traps.c index 59d0e320d6..c43aafea23 100644 --- a/xen/arch/x86/traps.c +++ b/xen/arch/x86/traps.c @@ -62,6 +62,8 @@ struct guest_trap_bounce { unsigned long eip; /* 12 */ } guest_trap_bounce[NR_CPUS] = { { 0 } }; +#if defined(__i386__) + #define DOUBLEFAULT_STACK_SIZE 1024 static struct tss_struct doublefault_tss; static unsigned char doublefault_stack[DOUBLEFAULT_STACK_SIZE]; @@ -906,3 +908,5 @@ unsigned long do_get_debugreg(int reg) if ( (reg < 0) || (reg > 7) ) return -EINVAL; return current->thread.debugreg[reg]; } + +#endif /* __i386__ */ diff --git a/xen/arch/x86/domain_page.c b/xen/arch/x86/x86_32/domain_page.c similarity index 100% rename from xen/arch/x86/domain_page.c rename to xen/arch/x86/x86_32/domain_page.c diff --git a/xen/arch/x86/entry.S b/xen/arch/x86/x86_32/entry.S similarity index 100% rename from xen/arch/x86/entry.S rename to xen/arch/x86/x86_32/entry.S diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/x86_32/mm.c similarity index 100% rename from xen/arch/x86/mm.c rename to xen/arch/x86/x86_32/mm.c diff --git a/xen/arch/x86/usercopy.c b/xen/arch/x86/x86_32/usercopy.c similarity index 100% rename from xen/arch/x86/usercopy.c rename to xen/arch/x86/x86_32/usercopy.c diff --git a/xen/arch/x86/xen.lds b/xen/arch/x86/x86_32/xen.lds similarity index 100% rename from xen/arch/x86/xen.lds rename to xen/arch/x86/x86_32/xen.lds diff --git a/xen/arch/x86/x86_64/entry.S b/xen/arch/x86/x86_64/entry.S new file mode 100644 index 0000000000..e69de29bb2 diff --git a/xen/arch/x86/x86_64/usercopy.c b/xen/arch/x86/x86_64/usercopy.c new file mode 100644 index 0000000000..e7c11fa501 --- /dev/null +++ b/xen/arch/x86/x86_64/usercopy.c @@ -0,0 +1,136 @@ +/* + * User address space access functions. + * + * Copyright 1997 Andi Kleen + * Copyright 1997 Linus Torvalds + * Copyright 2002 Andi Kleen + */ +#include + +/* + * Copy a null terminated string from userspace. + */ + +#define __do_strncpy_from_user(dst,src,count,res) \ +do { \ + long __d0, __d1, __d2; \ + __asm__ __volatile__( \ + " testq %1,%1\n" \ + " jz 2f\n" \ + "0: lodsb\n" \ + " stosb\n" \ + " testb %%al,%%al\n" \ + " jz 1f\n" \ + " decq %1\n" \ + " jnz 0b\n" \ + "1: subq %1,%0\n" \ + "2:\n" \ + ".section .fixup,\"ax\"\n" \ + "3: movq %5,%0\n" \ + " jmp 2b\n" \ + ".previous\n" \ + ".section __ex_table,\"a\"\n" \ + " .align 8\n" \ + " .quad 0b,3b\n" \ + ".previous" \ + : "=r"(res), "=c"(count), "=&a" (__d0), "=&S" (__d1), \ + "=&D" (__d2) \ + : "i"(-EFAULT), "0"(count), "1"(count), "3"(src), "4"(dst) \ + : "memory"); \ +} while (0) + +long +__strncpy_from_user(char *dst, const char *src, long count) +{ + long res; + __do_strncpy_from_user(dst, src, count, res); + return res; +} + +long +strncpy_from_user(char *dst, const char *src, long count) +{ + long res = -EFAULT; + if (access_ok(VERIFY_READ, src, 1)) + __do_strncpy_from_user(dst, src, count, res); + return res; +} + +/* + * Zero Userspace + */ + +unsigned long __clear_user(void *addr, unsigned long size) +{ + long __d0; + /* no memory constraint because it doesn't change any memory gcc knows + about */ + asm volatile( + " testq %[size8],%[size8]\n" + " jz 4f\n" + "0: movq %[zero],(%[dst])\n" + " addq %[eight],%[dst]\n" + " decl %%ecx ; jnz 0b\n" + "4: movq %[size1],%%rcx\n" + " testl %%ecx,%%ecx\n" + " jz 2f\n" + "1: movb %b[zero],(%[dst])\n" + " incq %[dst]\n" + " decl %%ecx ; jnz 1b\n" + "2:\n" + ".section .fixup,\"ax\"\n" + "3: lea 0(%[size1],%[size8],8),%[size8]\n" + " jmp 2b\n" + ".previous\n" + ".section __ex_table,\"a\"\n" + " .align 8\n" + " .quad 0b,3b\n" + " .quad 1b,2b\n" + ".previous" + : [size8] "=c"(size), [dst] "=&D" (__d0) + : [size1] "r"(size & 7), "[size8]" (size / 8), "[dst] "(addr), + [zero] "r" (0UL), [eight] "r" (8UL)); + return size; +} + + +unsigned long clear_user(void *to, unsigned long n) +{ + if (access_ok(VERIFY_WRITE, to, n)) + return __clear_user(to, n); + return n; +} + +/* + * Return the size of a string (including the ending 0) + * + * Return 0 on exception, a value greater than N if too long + */ + +long strnlen_user(const char *s, long n) +{ + unsigned long res = 0; + char c; + + if (!access_ok(VERIFY_READ, s, n)) + return 0; + + while (1) { + if (get_user(c, s)) + return 0; + if (!c) + return res+1; + if (res>n) + return n+1; + res++; + s++; + } +} + +unsigned long copy_in_user(void *to, const void *from, unsigned len) +{ + if (access_ok(VERIFY_WRITE, to, len) && access_ok(VERIFY_READ, from, len)) { + return copy_user_generic(to, from, len); + } + return len; +} diff --git a/xen/arch/x86/x86_64/xen.lds b/xen/arch/x86/x86_64/xen.lds new file mode 100644 index 0000000000..caea240e52 --- /dev/null +++ b/xen/arch/x86/x86_64/xen.lds @@ -0,0 +1,85 @@ +/* Excerpts written by Martin Mares */ +OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64", "elf64-x86-64") +OUTPUT_ARCH(i386:x86-64) +ENTRY(start) +SECTIONS +{ + . = 0xFFFF840000100000; + _text = .; /* Text and read-only data */ + .text : { + *(.text) + *(.fixup) + *(.gnu.warning) + } = 0x9090 + .text.lock : { *(.text.lock) } /* out-of-line lock text */ + + _etext = .; /* End of text section */ + + .rodata : { *(.rodata) *(.rodata.*) } + .kstrtab : { *(.kstrtab) } + + . = ALIGN(16); /* Exception table */ + __start___ex_table = .; + __ex_table : { *(__ex_table) } + __stop___ex_table = .; + + __start___ksymtab = .; /* Kernel symbol table */ + __ksymtab : { *(__ksymtab) } + __stop___ksymtab = .; + + __start___kallsyms = .; /* All kernel symbols */ + __kallsyms : { *(__kallsyms) } + __stop___kallsyms = .; + + .data : { /* Data */ + *(.data) + CONSTRUCTORS + } + + _edata = .; /* End of data section */ + + . = ALIGN(8192); /* init_task */ + .data.init_task : { *(.data.init_task) } + + . = ALIGN(4096); /* Init code and data */ + __init_begin = .; + .text.init : { *(.text.init) } + .data.init : { *(.data.init) } + . = ALIGN(16); + __setup_start = .; + .setup.init : { *(.setup.init) } + __setup_end = .; + __initcall_start = .; + .initcall.init : { *(.initcall.init) } + __initcall_end = .; + . = ALIGN(4096); + __init_end = .; + + . = ALIGN(4096); + .data.page_aligned : { *(.data.idt) } + + . = ALIGN(32); + .data.cacheline_aligned : { *(.data.cacheline_aligned) } + + __bss_start = .; /* BSS */ + .bss : { + *(.bss) + } + _end = . ; + + /* Sections to be discarded */ + /DISCARD/ : { + *(.text.exit) + *(.data.exit) + *(.exitcall.exit) + } + + /* Stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } +} diff --git a/xen/include/asm-x86/config.h b/xen/include/asm-x86/config.h index f61f6aeedd..f8cd450451 100644 --- a/xen/include/asm-x86/config.h +++ b/xen/include/asm-x86/config.h @@ -18,8 +18,10 @@ #define CONFIG_ACPI_BOOT 1 #define CONFIG_PCI 1 -#define CONFIG_PCI_BIOS 1 #define CONFIG_PCI_DIRECT 1 +#if defined(__i386__) +#define CONFIG_PCI_BIOS 1 +#endif #define CONFIG_IDE 1 #define CONFIG_BLK_DEV_IDE 1 diff --git a/xen/include/asm-x86/irq.h b/xen/include/asm-x86/irq.h index 7c5212e50f..1e35752a84 100644 --- a/xen/include/asm-x86/irq.h +++ b/xen/include/asm-x86/irq.h @@ -86,6 +86,8 @@ extern char _stext, _etext; #define __STR(x) #x #define STR(x) __STR(x) +#if defined(__i386__) + #define SAVE_ALL \ "cld\n\t" \ "pushl %gs\n\t" \ @@ -105,8 +107,11 @@ extern char _stext, _etext; "movl %edx,%fs\n\t" \ "movl %edx,%gs\n\t" -#define IRQ_NAME2(nr) nr##_interrupt(void) -#define IRQ_NAME(nr) IRQ_NAME2(IRQ##nr) +#else + +#define SAVE_ALL + +#endif #define BUILD_SMP_INTERRUPT(x,v) XBUILD_SMP_INTERRUPT(x,v) #define XBUILD_SMP_INTERRUPT(x,v)\ @@ -115,7 +120,7 @@ asmlinkage void call_##x(void); \ __asm__( \ "\n"__ALIGN_STR"\n" \ SYMBOL_NAME_STR(x) ":\n\t" \ - "pushl $"#v"\n\t" \ + "push"__OS" $"#v"\n\t" \ SAVE_ALL \ SYMBOL_NAME_STR(call_##x)":\n\t" \ "call "SYMBOL_NAME_STR(smp_##x)"\n\t" \ @@ -128,13 +133,13 @@ asmlinkage void call_##x(void); \ __asm__( \ "\n"__ALIGN_STR"\n" \ SYMBOL_NAME_STR(x) ":\n\t" \ - "pushl $"#v"\n\t" \ + "push"__OS" $"#v"\n\t" \ SAVE_ALL \ - "movl %esp,%eax\n\t" \ - "pushl %eax\n\t" \ + "mov %"__OP"sp,%"__OP"ax\n\t" \ + "push %"__OP"ax\n\t" \ SYMBOL_NAME_STR(call_##x)":\n\t" \ "call "SYMBOL_NAME_STR(smp_##x)"\n\t" \ - "addl $4,%esp\n\t" \ + "add $4,%"__OP"sp\n\t" \ "jmp ret_from_intr\n"); #define BUILD_COMMON_IRQ() \ @@ -147,12 +152,15 @@ __asm__( \ "call " SYMBOL_NAME_STR(do_IRQ) "\n\t" \ "jmp ret_from_intr\n"); +#define IRQ_NAME2(nr) nr##_interrupt(void) +#define IRQ_NAME(nr) IRQ_NAME2(IRQ##nr) + #define BUILD_IRQ(nr) \ asmlinkage void IRQ_NAME(nr); \ __asm__( \ "\n"__ALIGN_STR"\n" \ SYMBOL_NAME_STR(IRQ) #nr "_interrupt:\n\t" \ - "pushl $"#nr"\n\t" \ + "push"__OS" $"#nr"\n\t" \ "jmp common_interrupt"); extern unsigned long prof_cpu_mask;