--- /dev/null
+CFLAGS := $(shell python3-config --cflags)
+
+
+pykafs.so: afs_xg.c afs_py.c afs_py.h
+ python3 compile_pykafs.py build
+
+afs_xg.c afs_py.c afs_py.h: afs_xg.h rxgen.pl
+ ./rxgen.pl afs_xg.h
+
+clean:
+ find \( -name "*~" -o -name "*.o" -o -name "*.so" \) -delete
+ rm -rf build/
+ rm -f afs_xg.c afs_py.c afs_py.h
--- /dev/null
+/* AF_RXRPC driver
+ *
+ * Copyright (C) 2014 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#define _XOPEN_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include "af_rxrpc.h"
+#include "rxgen.h"
+
+/*
+ * dump the control messages
+ */
+static __attribute__((unused))
+void dump_cmsg(struct msghdr *msg)
+{
+ struct cmsghdr *cmsg;
+ unsigned long user_id;
+ unsigned char *p;
+ int abort_code;
+ int n;
+
+ for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) {
+ n = cmsg->cmsg_len - CMSG_ALIGN(sizeof(*cmsg));
+ p = CMSG_DATA(cmsg);
+
+ printf("CMSG: %zu: ", cmsg->cmsg_len);
+
+ if (cmsg->cmsg_level == SOL_RXRPC) {
+ switch (cmsg->cmsg_type) {
+ case RXRPC_USER_CALL_ID:
+ printf("RXRPC_USER_CALL_ID: ");
+ if (n != sizeof(user_id))
+ goto dump_data;
+ memcpy(&user_id, p, sizeof(user_id));
+ printf("%lx\n", user_id);
+ continue;
+
+ case RXRPC_ABORT:
+ printf("RXRPC_ABORT: ");
+ if (n != sizeof(abort_code))
+ goto dump_data;
+ memcpy(&abort_code, p, sizeof(abort_code));
+ printf("%d\n", abort_code);
+ continue;
+
+ case RXRPC_ACK:
+ printf("RXRPC_ACK");
+ if (n != 0)
+ goto dump_data_colon;
+ goto print_nl;
+
+ case RXRPC_RESPONSE:
+ printf("RXRPC_RESPONSE");
+ if (n != 0)
+ goto dump_data_colon;
+ goto print_nl;
+
+ case RXRPC_NET_ERROR:
+ printf("RXRPC_NET_ERROR: ");
+ if (n != sizeof(abort_code))
+ goto dump_data;
+ memcpy(&abort_code, p, sizeof(abort_code));
+ printf("%s\n", strerror(abort_code));
+ continue;
+
+ case RXRPC_BUSY:
+ printf("RXRPC_BUSY");
+ if (n != 0)
+ goto dump_data_colon;
+ goto print_nl;
+
+ case RXRPC_LOCAL_ERROR:
+ printf("RXRPC_LOCAL_ERROR: ");
+ if (n != sizeof(abort_code))
+ goto dump_data;
+ memcpy(&abort_code, p, sizeof(abort_code));
+ printf("%s\n", strerror(abort_code));
+ continue;
+
+ default:
+ break;
+ }
+ }
+
+ printf("l=%d t=%d", cmsg->cmsg_level, cmsg->cmsg_type);
+
+ dump_data_colon:
+ printf(": ");
+ dump_data:
+ printf("{");
+ for (; n > 0; n--, p++)
+ printf("%02x", *p);
+
+ print_nl:
+ printf("}\n");
+ }
+}
+
+/*
+ * Open a transport endpoint.
+ */
+static int open_af_rxrpc_endpoint(int local_port, unsigned short service, int exclusive)
+{
+ struct sockaddr_rxrpc srx;
+ int fd, ret;
+
+ fd = socket(AF_RXRPC, SOCK_DGRAM, PF_INET);
+ if (fd < 0)
+ return -1;
+
+ if (exclusive) {
+ ret = setsockopt(fd, SOL_RXRPC, RXRPC_EXCLUSIVE_CONNECTION,
+ NULL, 0);
+ if (ret == -1)
+ return -1;
+ }
+
+ /* Bind an address to the local endpoint */
+ srx.srx_family = AF_RXRPC;
+ srx.srx_service = service;
+ srx.transport_type = SOCK_DGRAM;
+ srx.transport_len = sizeof(srx.transport.sin);
+ srx.transport.sin.sin_family = AF_INET;
+ srx.transport.sin.sin_port = htons(local_port);
+ memset(&srx.transport.sin.sin_addr, 0, 4);
+
+ ret = bind(fd, (struct sockaddr *)&srx, sizeof(srx));
+ if (ret < 0)
+ return -1;
+
+ return fd;
+}
+
+/*
+ * Make a simple synchronous call.
+ */
+int rxrpc_simple_sync_call(struct sockaddr_rxrpc *srx,
+ int local_port, int exclusive,
+ const void *request, size_t reqlen,
+ void *reply, size_t replen,
+ uint32_t *_abort_code)
+{
+ struct cmsghdr *cmsg;
+ struct msghdr msg;
+ struct iovec iov[2];
+ size_t ctrllen, received;
+ unsigned char control[128], overrun[sizeof(long)];
+ void *preply;
+ int endpointfd;
+ int ret, got_eor;
+
+ endpointfd = open_af_rxrpc_endpoint(local_port, 0, exclusive);
+ if (endpointfd < 0)
+ return -1;
+
+ srx->srx_family = AF_RXRPC;
+ srx->transport_type = SOCK_DGRAM;
+
+ /* request an operation */
+ ctrllen = 0;
+ RXRPC_ADD_CALLID(control, ctrllen, 0x12345);
+
+ iov[0].iov_base = (void *)request;
+ iov[0].iov_len = reqlen;
+
+ msg.msg_name = srx;
+ msg.msg_namelen = sizeof(*srx);
+ msg.msg_iov = iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = control;
+ msg.msg_controllen = ctrllen;
+ msg.msg_flags = 0;
+
+ //dump_cmsg(&msg);
+
+ ret = sendmsg(endpointfd, &msg, 0);
+ if (ret == -1) {
+ close(endpointfd);
+ return -1;
+ }
+
+ /* wait for a reply */
+ preply = reply;
+ received = 0;
+ got_eor = 0;
+ while (!got_eor) {
+ iov[0].iov_base = preply;
+ iov[0].iov_len = replen - received;
+ iov[1].iov_base = &overrun;
+ iov[1].iov_len = sizeof(overrun);
+
+ if (replen - received > 0) {
+ msg.msg_iov = iov;
+ msg.msg_iovlen = 2;
+ } else {
+ msg.msg_iov = iov + 1;
+ msg.msg_iovlen = 1;
+ }
+
+ msg.msg_name = srx;
+ msg.msg_namelen = sizeof(*srx);
+ msg.msg_control = control;
+ msg.msg_controllen = sizeof(control);
+ msg.msg_flags = 0;
+
+ ret = recvmsg(endpointfd, &msg, 0);
+ if (ret == -1)
+ return -1;
+
+ //printf("RECV: %d [fl:%d]\n", ret, msg.msg_flags);
+ //printf("CMSG: %zu\n", msg.msg_controllen);
+ //printf("IOV: %zu [0]=%zu\n", msg.msg_iovlen, iov[0].iov_len);
+
+ if (msg.msg_flags & MSG_EOR)
+ got_eor = 1;
+
+ for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+ unsigned char *p;
+ int n;
+
+ if (cmsg->cmsg_level != SOL_RXRPC)
+ continue;
+
+ n = cmsg->cmsg_len - CMSG_ALIGN(sizeof(*cmsg));
+ p = CMSG_DATA(cmsg);
+
+ switch (cmsg->cmsg_type) {
+ case RXRPC_ABORT:
+ if (n != sizeof(*_abort_code))
+ goto internal_error;
+ memcpy(_abort_code, p, sizeof(*_abort_code));
+ ret = ECONNABORTED;
+ goto error;
+
+ case RXRPC_NET_ERROR:
+ case RXRPC_LOCAL_ERROR:
+ if (n != sizeof(ret))
+ goto internal_error;
+ memcpy(&ret, p, sizeof(ret));
+ goto error;
+
+ case RXRPC_BUSY:
+ ret = ECONNREFUSED;
+ goto error;
+
+ default:
+ break;
+ }
+ }
+
+ if (ret > replen - received) {
+ ret = EMSGSIZE;
+ goto error;
+ }
+
+ preply += ret;
+ received += ret;
+ }
+
+ close(endpointfd);
+ return received;
+
+internal_error:
+ ret = EBADMSG;
+error:
+ errno = ret;
+ close(endpointfd);
+ return -1;
+}
+
+/*
+ * Set up a new connection
+ */
+struct rx_connection *rx_new_connection(const struct sockaddr *sa,
+ socklen_t salen,
+ uint16_t service,
+ uint16_t local_port,
+ uint16_t local_service,
+ int exclusive)
+{
+ struct sockaddr_rxrpc srx;
+ struct rx_connection *z_conn;
+ int ret;
+
+ z_conn = calloc(1, sizeof(*z_conn));
+ if (!z_conn)
+ return NULL;
+
+ z_conn->peer.srx_family = AF_RXRPC;
+ z_conn->peer.srx_service = service;
+ z_conn->peer.transport_type = SOCK_DGRAM;
+ z_conn->peer.transport_len = sizeof(srx.transport.sin);
+
+ switch (sa->sa_family) {
+ case 0:
+ errno = EDESTADDRREQ;
+ goto error_conn;
+ case AF_INET:
+ if (salen != sizeof(struct sockaddr_in))
+ goto inval;
+ break;
+ case AF_INET6:
+ if (salen != sizeof(struct sockaddr_in6))
+ goto inval;
+ break;
+ default:
+ errno = EPROTOTYPE;
+ goto error_conn;
+ }
+
+ memcpy(&z_conn->peer.transport, sa, salen);
+ switch (sa->sa_family) {
+ case AF_INET:
+ if (!z_conn->peer.transport.sin.sin_port) {
+ errno = EDESTADDRREQ;
+ goto error_conn;
+ }
+ break;
+ case AF_INET6:
+ if (!z_conn->peer.transport.sin6.sin6_port) {
+ errno = EDESTADDRREQ;
+ goto error_conn;
+ }
+ break;
+ }
+
+ /* Open up a socket for talking to the AF_RXRPC module */
+ z_conn->fd = socket(AF_RXRPC, SOCK_DGRAM, PF_INET);
+ if (z_conn->fd < 0)
+ goto error_conn;
+
+ if (exclusive) {
+ ret = setsockopt(z_conn->fd,
+ SOL_RXRPC, RXRPC_EXCLUSIVE_CONNECTION,
+ NULL, 0);
+ if (ret == -1)
+ goto error_conn;
+ }
+
+ /* Bind an address to the local endpoint */
+ memset(&srx, 0, sizeof(srx));
+ srx.srx_family = AF_RXRPC;
+ srx.srx_service = local_service;
+ srx.transport_type = SOCK_DGRAM;
+ srx.transport_len = salen;
+ srx.transport.sin.sin_family = sa->sa_family;
+ switch (sa->sa_family) {
+ case AF_INET:
+ srx.transport.sin.sin_port = htons(local_port);
+ break;
+ case AF_INET6:
+ srx.transport.sin6.sin6_port = htons(local_port);
+ break;
+ }
+
+ ret = bind(z_conn->fd, (struct sockaddr *)&srx, sizeof(srx));
+ if (ret < 0)
+ goto error_fd;
+
+ return z_conn;
+
+inval:
+ errno = EINVAL;
+ goto error_conn;
+error_fd:
+ close(z_conn->fd);
+error_conn:
+ free(z_conn);
+ return NULL;
+}
+
+/*
+ * Close an RxRPC client connection. This will cause all outstanding
+ * operations to be aborted by the kernel..
+ */
+void rx_close_connection(struct rx_connection *z_conn)
+{
+ close(z_conn->fd);
+ free(z_conn);
+}
+
+/*
+ * Send a request off to the service.
+ */
+int rxrpc_send_request(struct rx_connection *z_conn,
+ struct rx_call *call,
+ const net_xdr_t *request,
+ size_t request_size,
+ size_t reply_buf_size)
+{
+ struct msghdr msg;
+ struct iovec iov[2];
+ size_t ctrllen;
+ unsigned char control[128];
+
+ call->got_eor = 0;
+ call->error_code = 0;
+ call->abort_code = 0;
+ call->reply_buf_size = reply_buf_size;
+ call->reply_size = 0;
+
+ /* request an operation */
+ ctrllen = 0;
+ RXRPC_ADD_CALLID(control, ctrllen, (unsigned long)call);
+
+ iov[0].iov_base = (void *)request;
+ iov[0].iov_len = request_size;
+
+ msg.msg_name = &z_conn->peer;
+ msg.msg_namelen = sizeof(z_conn->peer);
+ msg.msg_iov = iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = control;
+ msg.msg_controllen = ctrllen;
+ msg.msg_flags = 0;
+
+ //dump_cmsg(&msg);
+
+ return sendmsg(z_conn->fd, &msg, 0) == -1 ? -1 : 0;
+}
+
+/*
+ * Wait for a reply to arrive from a single synchronous RPC request.
+ */
+int rxrpc_wait_for_sync_reply(struct rx_connection *z_conn,
+ struct rx_call *call)
+{
+ struct sockaddr_rxrpc srx;
+ struct cmsghdr *cmsg;
+ struct msghdr msg;
+ struct iovec iov[2];
+ size_t space;
+ unsigned char control[128], overrun[sizeof(long)];
+ int ret;
+
+ /* wait for a reply */
+ while (!call->got_eor) {
+ space = call->reply_buf_size - call->reply_size;
+ iov[0].iov_base = call->reply + call->reply_size;
+ iov[0].iov_len = space;
+ iov[1].iov_base = &overrun;
+ iov[1].iov_len = sizeof(overrun);
+
+ if (iov[0].iov_len > 0) {
+ msg.msg_iov = iov;
+ msg.msg_iovlen = 2;
+ } else {
+ msg.msg_iov = iov + 1;
+ msg.msg_iovlen = 1;
+ }
+
+ memcpy(&srx, &z_conn->peer, sizeof(struct sockaddr_rxrpc));
+ msg.msg_name = &srx;
+ msg.msg_namelen = sizeof(srx);
+ msg.msg_control = control;
+ msg.msg_controllen = sizeof(control);
+ msg.msg_flags = 0;
+
+ ret = recvmsg(z_conn->fd, &msg, 0);
+ if (ret == -1)
+ return -1;
+
+ //printf("RECV: %d [fl:%d]\n", ret, msg.msg_flags);
+ //printf("CMSG: %zu\n", msg.msg_controllen);
+ //printf("IOV: %zu [0]=%zu\n", msg.msg_iovlen, iov[0].iov_len);
+
+ if (msg.msg_flags & MSG_EOR)
+ call->got_eor = 1;
+
+ for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+ unsigned char *p;
+ int n;
+
+ if (cmsg->cmsg_level != SOL_RXRPC)
+ continue;
+
+ n = cmsg->cmsg_len - CMSG_ALIGN(sizeof(*cmsg));
+ p = CMSG_DATA(cmsg);
+
+ switch (cmsg->cmsg_type) {
+ case RXRPC_ABORT:
+ if (n != sizeof(call->abort_code))
+ call->abort_code = 0;
+ else
+ memcpy(&call->abort_code, p,
+ sizeof(call->abort_code));
+ z_conn->last_abort_code = call->abort_code;
+ errno = ECONNABORTED;
+ return call->abort_code;;
+
+ case RXRPC_NET_ERROR:
+ case RXRPC_LOCAL_ERROR:
+ if (n != sizeof(ret)) {
+ errno = EBADMSG;
+ return -1;
+ }
+ memcpy(&ret, p, sizeof(ret));
+ errno = ret;
+ return -1;
+
+ case RXRPC_BUSY:
+ errno = ECONNREFUSED;
+ return -1;
+
+ default:
+ break;
+ }
+ }
+
+ if (ret > space) {
+ errno = EMSGSIZE;
+ return -1;
+ }
+
+ call->reply_size += ret;
+ }
+
+ return 0;
+}
--- /dev/null
+/* AF_RXRPC definitions
+ *
+ * Copyright (C) 2014 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#ifndef AF_RXRPC_H
+#define AF_RXRPC_H
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+/*
+ * Rx kerberos security abort codes
+ * - unfortunately we have no generalised security abort codes to say things
+ * like "unsupported security", so we have to use these instead and hope the
+ * other side understands
+ */
+#define RXKADINCONSISTENCY 19270400 /* security module structure inconsistent */
+#define RXKADPACKETSHORT 19270401 /* packet too short for security challenge */
+#define RXKADLEVELFAIL 19270402 /* security level negotiation failed */
+#define RXKADTICKETLEN 19270403 /* ticket length too short or too long */
+#define RXKADOUTOFSEQUENCE 19270404 /* packet had bad sequence number */
+#define RXKADNOAUTH 19270405 /* caller not authorised */
+#define RXKADBADKEY 19270406 /* illegal key: bad parity or weak */
+#define RXKADBADTICKET 19270407 /* security object was passed a bad ticket */
+#define RXKADUNKNOWNKEY 19270408 /* ticket contained unknown key version number */
+#define RXKADEXPIRED 19270409 /* authentication expired */
+#define RXKADSEALEDINCON 19270410 /* sealed data inconsistent */
+#define RXKADDATALEN 19270411 /* user data too long */
+#define RXKADILLEGALLEVEL 19270412 /* caller not authorised to use encrypted conns */
+
+/*
+ * AF_RXRPC socket address type.
+ */
+struct sockaddr_rxrpc {
+ sa_family_t srx_family; /* address family (AF_RXRPC) */
+ unsigned short srx_service; /* service desired */
+ unsigned short transport_type; /* type of transport socket (SOCK_DGRAM) */
+ unsigned short transport_len; /* length of transport address */
+ union {
+ sa_family_t family; /* transport address family */
+ struct sockaddr_in sin; /* IPv4 transport address */
+ struct sockaddr_in6 sin6; /* IPv6 transport address */
+ } transport;
+};
+
+/* cmsg_level value and sockopt level */
+#define SOL_RXRPC 272
+
+/* cmsg_type values */
+#define RXRPC_USER_CALL_ID 1 /* User call ID specifier */
+#define RXRPC_ABORT 2 /* Abort request / notification */
+#define RXRPC_ACK 3 /* [Server] RPC op final ACK received */
+#define RXRPC_RESPONSE 4 /* [Server] security response received */
+#define RXRPC_NET_ERROR 5 /* network error received */
+#define RXRPC_BUSY 6 /* server busy received */
+#define RXRPC_LOCAL_ERROR 7 /* local error generated */
+#define RXRPC_PREPARE_CALL_SLOT 8 /* Propose user call ID specifier for next call */
+
+/* sockopt optname */
+#define RXRPC_SECURITY_KEY 1 /* [clnt] set client security key */
+#define RXRPC_SECURITY_KEYRING 2 /* [srvr] set ring of server security keys */
+#define RXRPC_EXCLUSIVE_CONNECTION 3 /* [clnt] use exclusive RxRPC connection */
+#define RXRPC_MIN_SECURITY_LEVEL 4 /* minimum security level */
+
+/* RXRPC_MIN_SECURITY_LEVEL sockopt values */
+#define RXRPC_SECURITY_PLAIN 0 /* plain secure-checksummed packets only */
+#define RXRPC_SECURITY_AUTH 1 /* authenticated packets */
+#define RXRPC_SECURITY_ENCRYPT 2 /* encrypted packets */
+
+/*
+ * Add a call ID to a control message sequence.
+ */
+#define RXRPC_ADD_CALLID(control, ctrllen, id) \
+do { \
+ void *__buffer = (control); \
+ unsigned long *__data; \
+ struct cmsghdr *__cmsg; \
+ __cmsg = __buffer + (ctrllen); \
+ __cmsg->cmsg_len = CMSG_LEN(sizeof(*__data)); \
+ __cmsg->cmsg_level = SOL_RXRPC; \
+ __cmsg->cmsg_type = RXRPC_USER_CALL_ID; \
+ __data = (void *)CMSG_DATA(__cmsg); \
+ *__data = (id); \
+ (ctrllen) += __cmsg->cmsg_len; \
+ \
+} while (0)
+
+/*
+ * Add an abort instruction to a control message sequence.
+ */
+#define RXRPC_ADD_ABORT(control, ctrllen, abort_code) \
+do { \
+ void *__buffer = (control); \
+ unsigned int *__data; \
+ struct cmsghdr *__cmsg; \
+ __cmsg = (void *)(control) + (ctrllen); \
+ __cmsg->cmsg_len = CMSG_LEN(sizeof((__data)); \
+ __cmsg->cmsg_level = SOL_RXRPC; \
+ __cmsg->cmsg_type = RXRPC_ABORT; \
+ __data = (void *)CMSG_DATA(__cmsg); \
+ *__data = (abort_code); \
+ (ctrllen) += __cmsg->cmsg_len; \
+ \
+} while (0)
+
+/*
+ * af_rxrpc.c
+ */
+extern int rxrpc_simple_sync_call(struct sockaddr_rxrpc *srx,
+ int local_port, int exclusive,
+ const void *request, size_t reqlen,
+ void *reply, size_t replen,
+ uint32_t *_abort_code);
+#endif /* AF_RXRPC_H */
--- /dev/null
+/* AFS common types
+ *
+ * Copyright (C) 2002, 2007 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef AFS_H
+#define AFS_H
+
+#include <netinet/in.h>
+
+#define AFS_MAXCELLNAME 64 /* maximum length of a cell name */
+#define AFS_MAXVOLNAME 64 /* maximum length of a volume name */
+#define AFSNAMEMAX 256 /* maximum length of a filename plus NUL */
+#define AFSPATHMAX 1024 /* maximum length of a pathname plus NUL */
+#define AFSOPAQUEMAX 1024 /* maximum length of an opaque field */
+
+typedef uint32_t afs_volid_t;
+typedef uint32_t afs_vnodeid_t;
+typedef uint64_t afs_dataversion_t;
+
+typedef enum {
+ AFSVL_RWVOL, /* read/write volume */
+ AFSVL_ROVOL, /* read-only volume */
+ AFSVL_BACKVOL, /* backup volume */
+} __attribute__((packed)) afs_voltype_t;
+
+typedef enum {
+ AFS_FTYPE_INVALID = 0,
+ AFS_FTYPE_FILE = 1,
+ AFS_FTYPE_DIR = 2,
+ AFS_FTYPE_SYMLINK = 3,
+} afs_file_type_t;
+
+typedef enum {
+ AFS_LOCK_READ = 0, /* read lock request */
+ AFS_LOCK_WRITE = 1, /* write lock request */
+} afs_lock_type_t;
+
+#define AFS_LOCKWAIT (5 * 60) /* time until a lock times out (seconds) */
+
+/*
+ * AFS file identifier
+ */
+struct afs_fid {
+ afs_volid_t vid; /* volume ID */
+ afs_vnodeid_t vnode; /* file index within volume */
+ unsigned unique; /* unique ID number (file index version) */
+};
+
+/*
+ * AFS callback notification
+ */
+typedef enum {
+ AFSCM_CB_UNTYPED = 0, /* no type set on CB break */
+ AFSCM_CB_EXCLUSIVE = 1, /* CB exclusive to CM [not implemented] */
+ AFSCM_CB_SHARED = 2, /* CB shared by other CM's */
+ AFSCM_CB_DROPPED = 3, /* CB promise cancelled by file server */
+} afs_callback_type_t;
+
+struct afs_callback {
+ struct afs_fid fid; /* file identifier */
+ unsigned version; /* callback version */
+ unsigned expiry; /* time at which expires */
+ afs_callback_type_t type; /* type of callback */
+};
+
+#define AFSCBMAX 50 /* maximum callbacks transferred per bulk op */
+
+/*
+ * AFS volume information
+ */
+struct afs_volume_info {
+ afs_volid_t vid; /* volume ID */
+ afs_voltype_t type; /* type of this volume */
+ afs_volid_t type_vids[5]; /* volume ID's for possible types for this vol */
+
+ /* list of fileservers serving this volume */
+ size_t nservers; /* number of entries used in servers[] */
+ struct {
+ struct in_addr addr; /* fileserver address */
+ } servers[8];
+};
+
+/*
+ * AFS security ACE access mask
+ */
+typedef unsigned afs_access_t;
+#define AFS_ACE_READ 0x00000001U /* - permission to read a file/dir */
+#define AFS_ACE_WRITE 0x00000002U /* - permission to write/chmod a file */
+#define AFS_ACE_INSERT 0x00000004U /* - permission to create dirent in a dir */
+#define AFS_ACE_LOOKUP 0x00000008U /* - permission to lookup a file/dir in a dir */
+#define AFS_ACE_DELETE 0x00000010U /* - permission to delete a dirent from a dir */
+#define AFS_ACE_LOCK 0x00000020U /* - permission to lock a file */
+#define AFS_ACE_ADMINISTER 0x00000040U /* - permission to change ACL */
+#define AFS_ACE_USER_A 0x01000000U /* - 'A' user-defined permission */
+#define AFS_ACE_USER_B 0x02000000U /* - 'B' user-defined permission */
+#define AFS_ACE_USER_C 0x04000000U /* - 'C' user-defined permission */
+#define AFS_ACE_USER_D 0x08000000U /* - 'D' user-defined permission */
+#define AFS_ACE_USER_E 0x10000000U /* - 'E' user-defined permission */
+#define AFS_ACE_USER_F 0x20000000U /* - 'F' user-defined permission */
+#define AFS_ACE_USER_G 0x40000000U /* - 'G' user-defined permission */
+#define AFS_ACE_USER_H 0x80000000U /* - 'H' user-defined permission */
+
+/*
+ * AFS file status information
+ */
+struct afs_file_status {
+ unsigned if_version; /* interface version */
+#define AFS_FSTATUS_VERSION 1
+
+ afs_file_type_t type; /* file type */
+ unsigned nlink; /* link count */
+ uint64_t size; /* file size */
+ afs_dataversion_t data_version; /* current data version */
+ uint32_t author; /* author ID */
+ uint32_t owner; /* owner ID */
+ uint32_t group; /* group ID */
+ afs_access_t caller_access; /* access rights for authenticated caller */
+ afs_access_t anon_access; /* access rights for unauthenticated caller */
+ uint32_t mode; /* UNIX mode */
+ struct afs_fid parent; /* parent dir ID for non-dirs only */
+ time_t mtime_client; /* last time client changed data */
+ time_t mtime_server; /* last time server changed data */
+ int32_t lock_count; /* file lock count (0=UNLK -1=WRLCK +ve=#RDLCK */
+};
+
+/*
+ * AFS file status change request
+ */
+
+#define AFS_SET_MTIME 0x01 /* set the mtime */
+#define AFS_SET_OWNER 0x02 /* set the owner ID */
+#define AFS_SET_GROUP 0x04 /* set the group ID (unsupported?) */
+#define AFS_SET_MODE 0x08 /* set the UNIX mode */
+#define AFS_SET_SEG_SIZE 0x10 /* set the segment size (unsupported) */
+
+/*
+ * AFS volume synchronisation information
+ */
+struct afs_volsync {
+ time_t creation; /* volume creation time */
+};
+
+/*
+ * AFS volume status record
+ */
+struct afs_volume_status {
+ uint32_t vid; /* volume ID */
+ uint32_t parent_id; /* parent volume ID */
+ uint8_t online; /* true if volume currently online and available */
+ uint8_t in_service; /* true if volume currently in service */
+ uint8_t blessed; /* same as in_service */
+ uint8_t needs_salvage; /* true if consistency checking required */
+ uint32_t type; /* volume type (afs_voltype_t) */
+ uint32_t min_quota; /* minimum space set aside (blocks) */
+ uint32_t max_quota; /* maximum space this volume may occupy (blocks) */
+ uint32_t blocks_in_use; /* space this volume currently occupies (blocks) */
+ uint32_t part_blocks_avail; /* space available in volume's partition */
+ uint32_t part_max_blocks; /* size of volume's partition */
+};
+
+#define AFS_BLOCK_SIZE 1024
+
+#endif /* AFS_H */
--- /dev/null
+/* AFS Cache Manager definitions
+ *
+ * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef AFS_CM_H
+#define AFS_CM_H
+
+#define AFS_CM_PORT 7001 /* AFS file server port */
+#define CM_SERVICE 1 /* AFS File Service ID */
+
+enum AFS_CM_Operations {
+ CBCallBack = 204, /* break callback promises */
+ CBInitCallBackState = 205, /* initialise callback state */
+ CBProbe = 206, /* probe client */
+ CBGetLock = 207, /* get contents of CM lock table */
+ CBGetCE = 208, /* get cache file description */
+ CBGetXStatsVersion = 209, /* get version of extended statistics */
+ CBGetXStats = 210, /* get contents of extended statistics data */
+ CBInitCallBackState3 = 213, /* initialise callback state, version 3 */
+ CBProbeUuid = 214, /* check the client hasn't rebooted */
+ CBTellMeAboutYourself = 65538, /* get client capabilities */
+};
+
+#define AFS_CAP_ERROR_TRANSLATION 0x1
+
+#endif /* AFS_CM_H */
--- /dev/null
+/* AFS File Service definitions
+ *
+ * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef AFS_FS_H
+#define AFS_FS_H
+
+#define AFS_FS_PORT 7000 /* AFS file server port */
+#define FS_SERVICE 1 /* AFS File Service ID */
+
+enum AFS_FS_Operations {
+ FSFETCHDATA = 130, /* AFS Fetch file data */
+ FSFETCHSTATUS = 132, /* AFS Fetch file status */
+ FSSTOREDATA = 133, /* AFS Store file data */
+ FSSTORESTATUS = 135, /* AFS Store file status */
+ FSREMOVEFILE = 136, /* AFS Remove a file */
+ FSCREATEFILE = 137, /* AFS Create a file */
+ FSRENAME = 138, /* AFS Rename or move a file or directory */
+ FSSYMLINK = 139, /* AFS Create a symbolic link */
+ FSLINK = 140, /* AFS Create a hard link */
+ FSMAKEDIR = 141, /* AFS Create a directory */
+ FSREMOVEDIR = 142, /* AFS Remove a directory */
+ FSGIVEUPCALLBACKS = 147, /* AFS Discard callback promises */
+ FSGETVOLUMEINFO = 148, /* AFS Get information about a volume */
+ FSGETVOLUMESTATUS = 149, /* AFS Get volume status information */
+ FSGETROOTVOLUME = 151, /* AFS Get root volume name */
+ FSSETLOCK = 156, /* AFS Request a file lock */
+ FSEXTENDLOCK = 157, /* AFS Extend a file lock */
+ FSRELEASELOCK = 158, /* AFS Release a file lock */
+ FSLOOKUP = 161, /* AFS lookup file in directory */
+ FSFETCHDATA64 = 65537, /* AFS Fetch file data */
+ FSSTOREDATA64 = 65538, /* AFS Store file data */
+};
+
+enum AFS_FS_Errors {
+ VSALVAGE = 101, /* volume needs salvaging */
+ VNOVNODE = 102, /* no such file/dir (vnode) */
+ VNOVOL = 103, /* no such volume or volume unavailable */
+ VVOLEXISTS = 104, /* volume name already exists */
+ VNOSERVICE = 105, /* volume not currently in service */
+ VOFFLINE = 106, /* volume is currently offline (more info available [VVL-spec]) */
+ VONLINE = 107, /* volume is already online */
+ VDISKFULL = 108, /* disk partition is full */
+ VOVERQUOTA = 109, /* volume's maximum quota exceeded */
+ VBUSY = 110, /* volume is temporarily unavailable */
+ VMOVED = 111, /* volume moved to new server - ask this FS where */
+};
+
+#endif /* AFS_FS_H */
--- /dev/null
+/* AFS Authentication Service client interface
+ *
+ * Copyright (C) 2014 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef AFS_KA_H
+#define AFS_KA_H
+
+#include "afs.h"
+
+#define AFS_KA_PORT 7004 /* Authentication service port */
+#define KA_SERVICE 731 /* RxRPC service ID */
+#define KA_TICKET_GRANTING_SERVICE 732
+
+enum AFSVL_Operations {
+ KAA_Authenticate = 21,
+ KAA_AuthenticateV2 = 22,
+ KAT_GetToken = 23,
+};
+
+/*
+ * rpc_authentication.c
+ */
+extern int afs_KA_call(struct sockaddr_rxrpc *srx,
+ const void *request, size_t reqlen, void *reply, size_t replen,
+ uint32_t *_abort_code);
+extern int afs_KAT_call(struct sockaddr_rxrpc *srx,
+ const void *request, size_t reqlen, void *reply, size_t replen,
+ uint32_t *_abort_code);
+
+#endif /* AFS_KA_H */
--- /dev/null
+/* AFS Volume Location Service client interface
+ *
+ * Copyright (C) 2002, 2007 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef AFS_VL_H
+#define AFS_VL_H
+
+#include "afs.h"
+
+#define AFS_VL_PORT 7003 /* volume location service port */
+#define VL_SERVICE 52 /* RxRPC service ID for the Volume Location service */
+
+enum AFSVL_Operations {
+ VLCREATEENTRY = 501, /* AFS Create VLDB entry */
+ VLDELETEENTRY = 502, /* AFS Delete VLDB entry */
+ VLGETENTRYBYID = 503, /* AFS Get Cache Entry By ID operation ID */
+ VLGETENTRYBYNAME = 504, /* AFS Get Cache Entry By Name operation ID */
+ VLPROBE = 514, /* AFS Probe Volume Location Service operation ID */
+};
+
+enum AFSVL_Errors {
+ AFSVL_IDEXIST = 363520, /* Volume Id entry exists in vl database */
+ AFSVL_IO = 363521, /* I/O related error */
+ AFSVL_NAMEEXIST = 363522, /* Volume name entry exists in vl database */
+ AFSVL_CREATEFAIL = 363523, /* Internal creation failure */
+ AFSVL_NOENT = 363524, /* No such entry */
+ AFSVL_EMPTY = 363525, /* Vl database is empty */
+ AFSVL_ENTDELETED = 363526, /* Entry is deleted (soft delete) */
+ AFSVL_BADNAME = 363527, /* Volume name is illegal */
+ AFSVL_BADINDEX = 363528, /* Index is out of range */
+ AFSVL_BADVOLTYPE = 363529, /* Bad volume type */
+ AFSVL_BADSERVER = 363530, /* Illegal server number (out of range) */
+ AFSVL_BADPARTITION = 363531, /* Bad partition number */
+ AFSVL_REPSFULL = 363532, /* Run out of space for Replication sites */
+ AFSVL_NOREPSERVER = 363533, /* No such Replication server site exists */
+ AFSVL_DUPREPSERVER = 363534, /* Replication site already exists */
+ AFSVL_RWNOTFOUND = 363535, /* Parent R/W entry not found */
+ AFSVL_BADREFCOUNT = 363536, /* Illegal Reference Count number */
+ AFSVL_SIZEEXCEEDED = 363537, /* Vl size for attributes exceeded */
+ AFSVL_BADENTRY = 363538, /* Bad incoming vl entry */
+ AFSVL_BADVOLIDBUMP = 363539, /* Illegal max volid increment */
+ AFSVL_IDALREADYHASHED = 363540, /* RO/BACK id already hashed */
+ AFSVL_ENTRYLOCKED = 363541, /* Vl entry is already locked */
+ AFSVL_BADVOLOPER = 363542, /* Bad volume operation code */
+ AFSVL_BADRELLOCKTYPE = 363543, /* Bad release lock type */
+ AFSVL_RERELEASE = 363544, /* Status report: last release was aborted */
+ AFSVL_BADSERVERFLAG = 363545, /* Invalid replication site server flag */
+ AFSVL_PERM = 363546, /* No permission access */
+ AFSVL_NOMEM = 363547, /* malloc/realloc failed to alloc enough memory */
+};
+
+/*
+ * maps to "struct vldbentry" in vvl-spec.pdf
+ */
+struct afs_vldbentry {
+ char name[AFS_MAXVOLNAME + 1]; /* name of volume (with NUL char) */
+ afs_voltype_t type; /* volume type */
+ unsigned num_servers; /* num servers that hold instances of this vol */
+ unsigned clone_id; /* cloning ID */
+
+ unsigned flags;
+#define AFS_VLF_RWEXISTS 0x1000 /* R/W volume exists */
+#define AFS_VLF_ROEXISTS 0x2000 /* R/O volume exists */
+#define AFS_VLF_BACKEXISTS 0x4000 /* backup volume exists */
+
+ afs_volid_t volume_ids[3]; /* volume IDs */
+
+ struct {
+ struct in_addr addr; /* server address */
+ unsigned partition; /* partition ID on this server */
+ unsigned flags; /* server specific flags */
+#define AFS_VLSF_NEWREPSITE 0x0001 /* unused */
+#define AFS_VLSF_ROVOL 0x0002 /* this server holds a R/O instance of the volume */
+#define AFS_VLSF_RWVOL 0x0004 /* this server holds a R/W instance of the volume */
+#define AFS_VLSF_BACKVOL 0x0008 /* this server holds a backup instance of the volume */
+ } servers[8];
+};
+
+/*
+ * rpc_vlocation.c
+ */
+struct sockaddr_rxrpc;
+extern int afs_VL_call(struct sockaddr *sa, socklen_t salen,
+ const void *request, size_t reqlen, void *reply, size_t replen,
+ uint32_t *_abort_code);
+extern int afs_VL_abort_to_error(uint32_t abort_code);
+extern int afs_VL_get_entry_by_name(struct sockaddr *sa, socklen_t salen,
+ const char *volname,
+ struct afs_vldbentry *entry,
+ uint32_t *_abort_code);
+
+#endif /* AFS_VL_H */
--- /dev/null
+/* AFS-3 RPC Interface definitions.
+ *
+ * The contents of this file are processed by a script to produce the interface
+ * routines.
+ *
+ * Copyright (C) 2014 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#ifndef _AFS_XG_H
+#define _AFS_XG_H
+
+#include "rxgen.h"
+
+#define AFS_VL_PORT 7003 /* volume location service port */
+#define VL_SERVICE 52 /* RxRPC service ID for the Volume Location service */
+#define AFS_CM_PORT 7001 /* AFS file server port */
+#define CM_SERVICE 1 /* AFS File Service ID */
+
+#define AFS_MAXCELLNAME 64 /* maximum length of a cell name */
+#define AFS_MAXVOLNAME 64 /* maximum length of a volume name */
+#define AFSNAMEMAX 256 /* maximum length of a filename plus NUL */
+#define AFSPATHMAX 1024 /* maximum length of a pathname plus NUL */
+#define AFSOPAQUEMAX 1024 /* maximum length of an opaque field */
+
+#define MAXTYPES 3
+#define MAXNSERVERS 8
+#define MAXNAMELEN 65
+
+enum AFSVL_Operations {
+ VLCREATEENTRY = 501, /* AFS Create VLDB entry */
+ VLDELETEENTRY = 502, /* AFS Delete VLDB entry */
+ VLGETENTRYBYID = 503, /* AFS Get Cache Entry By ID operation ID */
+ VLGETENTRYBYNAME = 504, /* AFS Get Cache Entry By Name operation ID */
+ VLGETNEWVOLUMEID = 505, /* AFS Generate a new volume ID */
+ VLPROBE = 514, /* AFS Probe Volume Location Service operation ID */
+};
+
+/*
+ * AFS Volume Location Database entry.
+ *
+ * As defined in AFS-3 Vol/VL Server Spec #3.3.1.
+ */
+struct vldbentry {
+ char name[MAXNAMELEN]; /* The volume name */
+ uint32_t volumeType; /* The volume type */
+ uint32_t nServers; /* The number of servers having an instance */
+ uint32_t serverNumber[MAXNSERVERS]; /* Server addresses */
+ uint32_t serverPartition[MAXNSERVERS]; /* Partition ID on corresponding servers */
+ uint32_t serverFlags[MAXNSERVERS]; /* Flags for corresponding servers */
+ uint32_t volumeId[MAXTYPES]; /* Volume ID for each type */
+ uint32_t cloneId; /* Cloning operation ID */
+ uint32_t flags; /* Flags indicating presence of fields */
+};
+
+/*
+ * AFS Volume Location Database entry update request.
+ *
+ * As defined in AFS-3 Vol/VL Server Spec #3.3.5.
+ */
+struct VldbUpdateEntry {
+ uint32_t Mask; /* Bit mask indicating fields to be affected */
+ char name[MAXNAMELEN]; /* The volume name */
+ uint32_t volumeType; /* The volume type */
+ uint32_t flags; /* Used in conjunction with Mask to select valid fields */
+ uint32_t ReadOnlyId; /* The read-only ID */
+ uint32_t BackupId; /* The backup ID */
+ uint32_t cloneId; /* The clone ID */
+ uint32_t nModifiedRepsites; /* Number of replication site entries to be changed */
+ uint32_t RepsitesMask[MAXNSERVERS]; /* Bit mask for each rep site */
+ uint32_t RepsitesTargetServer[MAXNSERVERS]; /* Target servers */
+ uint32_t RepsitesTargetPart[MAXNSERVERS]; /* Target server partitions */
+ uint32_t RepsitesNewServer[MAXNSERVERS]; /* New server sites */
+ uint32_t RepsitesNewPart[MAXNSERVERS]; /* New server partitions */
+ uint32_t RepsitesNewFlags[MAXNSERVERS]; /* Flags applying to each new site */
+};
+
+/*
+ * AFS VLDB list query by-attribute filter.
+ *
+ * As defined in AFS-3 Vol/VL Server Spec #3.3.6.
+ */
+struct VldbListByAttributes {
+ uint32_t Mask; /* Bit mask used to select attr fields to match */
+ uint32_t server; /* The server address to match */
+ uint32_t partition; /* The partition ID to match */
+ uint32_t volumetype; /* The volume type to match */
+ uint32_t volumeid; /* The volume ID to match */
+ uint32_t flag; /* Flags concerning these values */
+};
+
+/*
+ * Create a VLDB entry (AFS-3 Vol/VL Server Spec #3.6.1)
+ *
+ * Operation: VLCREATEENTRY
+ */
+extern int VL_CreateEntry(
+ struct rx_connection *z_conn,
+ /*IN*/ const struct vldbentry *newentry);
+
+/*
+ * Delete a VLDB entry (AFS-3 Vol/VL Server Spec #3.6.2)
+ *
+ * Operation: VLDELETEENTRY
+ */
+extern int VL_DeleteEntry(
+ struct rx_connection *z_conn,
+ /*IN*/ uint32_t Volid,
+ /*IN*/ uint32_t voltype);
+
+/*
+ * Get a VLDB entry by ID/type (AFS-3 Vol/VL Server Spec #3.6.3)
+ *
+ * Operation: VLGETENTRYBYID
+ */
+extern int VL_GetEntryByID(
+ struct rx_connection *z_conn,
+ /*IN*/ uint32_t Volid,
+ /*IN*/ uint32_t voltype,
+ /*OUT*/ struct vldbentry *entry);
+
+/*
+ * Get a VLDB entry by volume name (AFS-3 Vol/VL Server Spec #3.6.4)
+ *
+ * Operation: VLGETENTRYBYNAME
+ */
+extern int VL_GetEntryByName(
+ struct rx_connection *z_conn,
+ /*IN*/ const char *volumename, /* Max MAXNAMELEN */
+ /*OUT*/ struct vldbentry *entry);
+
+/*
+ * Generate a new volume ID (AFS-3 Vol/VL Server Spec #3.6.5)
+ *
+ * Operation: VLGETNEWVOLUMEID
+ */
+extern int VL_GetNewVolumeId(
+ struct rx_connection *z_conn,
+ /*IN*/ uint32_t bumpcount,
+ /*OUT*/ uint32_t *newvolumid);
+
+/*
+ * Probe an AFS VL server (AFS-3 Vol/VL Server Spec #3.6.14)
+ *
+ * Operation: VLPROBE
+ */
+extern int VL_Probe(
+ struct rx_connection *z_conn);
+
+#endif /* _AFS_XG_H */
--- /dev/null
+#!/usr/bin/python3
+
+import dns.resolver;
+
+vladdrs = [];
+answers = dns.resolver.query('grand.central.org', 'AFSDB');
+for record in answers:
+ print("--", record.hostname, "--");
+ A_recs = dns.resolver.query(record.hostname, 'A');
+ for addr in A_recs:
+ vladdrs.append(addr.address);
+ print(vladdrs);
--- /dev/null
+from distutils.core import setup, Extension
+setup(name="kafs", version="0.1",
+ ext_modules=[Extension("kafs",
+ sources = [ "kafs.c",
+ "afs_py.c",
+ "py_rxgen.c",
+ "afs_xg.c",
+ "af_rxrpc.c"
+ ],
+ )])
--- /dev/null
+/* kAFS python module
+ *
+ * Copyright (C) 2014 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#include <Python.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include "structmember.h"
+#include "kafs.h"
+#include "afs_py.h"
+
+#if 0
+/*
+ * The static methods.
+ */
+static PyMethodDef module_methods[] = {
+ {"new_vlocation", (PyCFunction)kafs_new_vlocation, METH_NOARGS,
+ "Create a new vlocation record."
+ },
+ {"VL_GetEntryByName", (PyCFunction)kafs_VL_GetEntryByName, METH_VARARGS,
+ "Look up an entry by name."
+ },
+ {NULL} /* Sentinel */
+};
+#endif
+
+/*
+ * Initialise the module.
+ */
+#ifndef PyMODINIT_FUNC /* declarations for DLL import/export */
+#define PyMODINIT_FUNC void
+#endif
+PyMODINIT_FUNC
+PyInit_kafs(void)
+{
+ PyObject *m;
+
+ m = pykafs_load_wrappers();
+ if (!m) {
+#if PY_VERSION_HEX >= 0x03000000
+ return NULL;
+#else
+ return;
+#endif
+ }
+
+#if 0
+ PyModule_AddIntConstant(m, "RWVOL", AFSVL_RWVOL);
+ PyModule_AddIntConstant(m, "ROVOL", AFSVL_ROVOL);
+ PyModule_AddIntConstant(m, "BACKVOL", AFSVL_BACKVOL);
+
+ PyModule_AddIntConstant(m, "RWVOL_EXISTS", AFS_VLF_RWEXISTS);
+ PyModule_AddIntConstant(m, "ROVOL_EXISTS", AFS_VLF_ROEXISTS);
+ PyModule_AddIntConstant(m, "BACKVOL_EXISTS", AFS_VLF_BACKEXISTS);
+
+ PyModule_AddIntConstant(m, "NEWREPSITE", AFS_VLSF_NEWREPSITE);
+ PyModule_AddIntConstant(m, "ROVOL_HERE", AFS_VLSF_ROVOL);
+ PyModule_AddIntConstant(m, "RWVOL_HERE", AFS_VLSF_RWVOL);
+ PyModule_AddIntConstant(m, "BACKVOL_HERE", AFS_VLSF_BACKVOL);
+#endif
+
+#if PY_VERSION_HEX >= 0x03000000
+ return m;
+#else
+ return;
+#endif
+}
--- /dev/null
+/* kAFS interface
+ *
+ * Copyright (C) 2014 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#ifndef _KAFS_H
+#define _KAFS_H
+
+/*
+ * type_vlocation.c
+ */
+extern PyTypeObject vlocationType;
+extern PyObject *kafs_new_vlocation(PyObject *_self, PyObject *args);
+extern PyObject *kafs_VL_GetEntryByName(PyObject *_self, PyObject *args);
+
+
+#endif /* _KAFS_H */
--- /dev/null
+/* rxgen Python wrapping support
+ *
+ * Copyright (C) 2014 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#include <Python.h>
+#include "structmember.h"
+#include <arpa/inet.h>
+#include "py_rxgen.h"
+#include "rxgen.h"
+
+PyObject *py_rxgen_get_string(const void *_array, size_t n)
+{
+ const char *array = _array;
+ return PyUnicode_FromStringAndSize(array, strnlen(array, n - 1));
+}
+
+int py_rxgen_set_string(void *_array, size_t n, PyObject *val)
+{
+ Py_ssize_t len;
+ char *array = _array;
+ char *new_name;
+
+ if (!PyUnicode_Check(val))
+ return -1;
+
+ new_name = PyUnicode_AsUTF8AndSize(val, &len);
+ if (!new_name)
+ return -1;
+
+ if (len < 0 || len > n - 1)
+ return -1;
+
+ memcpy(array, new_name, len);
+ memset(array + len, 0, n - len);
+ return 0;
+}
+
+PyObject *py_rxgen_get_uint8(const void *_array, size_t n)
+{
+ PyObject *list;
+ const uint8_t *array = _array;
+ int i;
+
+ list = PyTuple_New(n);
+ if (!list)
+ return NULL;
+
+ for (i = 0; i < n; i++) {
+ PyObject *num = PyLong_FromLong(array[i]);
+ if (!num)
+ goto error;
+
+ if (PyTuple_SetItem(list, i, num) != 0) {
+ Py_DECREF(num);
+ goto error;
+ }
+ }
+ return list;
+
+error:
+ Py_DECREF(list);
+ return NULL;
+}
+
+PyObject *py_rxgen_get_uint16(const void *_array, size_t n)
+{
+ PyObject *list;
+ const uint16_t *array = _array;
+ int i;
+
+ list = PyTuple_New(n);
+ if (!list)
+ return NULL;
+
+ for (i = 0; i < n; i++) {
+ PyObject *num = PyLong_FromLong(array[i]);
+ if (!num)
+ goto error;
+
+ if (PyTuple_SetItem(list, i, num) != 0) {
+ Py_DECREF(num);
+ goto error;
+ }
+ }
+ return list;
+
+error:
+ Py_DECREF(list);
+ return NULL;
+}
+
+PyObject *py_rxgen_get_uint32(const void *_array, size_t n)
+{
+ PyObject *list;
+ const uint32_t *array = _array;
+ int i;
+
+ list = PyTuple_New(n);
+ if (!list)
+ return NULL;
+
+ for (i = 0; i < n; i++) {
+ PyObject *num = PyLong_FromLong(array[i]);
+ if (!num)
+ goto error;
+
+ if (PyTuple_SetItem(list, i, num) != 0) {
+ Py_DECREF(num);
+ goto error;
+ }
+ }
+ return list;
+
+error:
+ Py_DECREF(list);
+ return NULL;
+}
+
+int py_rxgen_set_uint32(void *_array, size_t n, PyObject *val)
+{
+ uint32_t *array = _array;
+ int i;
+
+ if (!PyTuple_Check(val)) {
+ PyErr_Format(PyExc_TypeError,
+ "Expected a tuple");
+ return -1;
+ }
+ if (PyTuple_GET_SIZE(val) != n) {
+ PyErr_Format(PyExc_ValueError,
+ "Expected a tuple of %zu size", n);
+ return -1;
+ }
+
+ PyErr_Clear();
+ for (i = 0; i < n; i++) {
+ PyObject *p = PyTuple_GET_ITEM(val, i);
+ unsigned long val = PyLong_AsUnsignedLong(p);
+
+ if (PyErr_Occurred())
+ return -1;
+ array[i] = val;
+ }
+
+ return 0;
+}
+
+/*
+ * RxRPC connection container.
+ */
+static int
+py_rx_connection_init(PyObject *_self, PyObject *args, PyObject *kwds)
+{
+ struct py_rx_connection *self = (struct py_rx_connection *)_self;
+ self->x = NULL;
+ return 0;
+}
+
+static void
+py_rx_connection_dealloc(struct py_rx_connection *self)
+{
+ if (self->x) {
+ rx_close_connection(self->x);
+ self->x = NULL;
+ }
+ Py_TYPE(self)->tp_free((PyObject *)self);
+}
+
+PyTypeObject py_rx_connectionType = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "kafs.rx_connection", /*tp_name*/
+ sizeof(struct py_rx_connection), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ (destructor)py_rx_connection_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ "RxRPC connection container", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ 0, /* tp_methods */
+ 0, /* tp_members */
+ 0, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ py_rx_connection_init, /* tp_init */
+ 0, /* tp_alloc */
+ 0, /* tp_new */
+};
+
+/*
+ * Set up an RxRPC connection.
+ */
+PyObject *
+kafs_py_rx_new_connection(PyObject *_self, PyObject *args)
+{
+ struct py_rx_connection *obj;
+ struct rx_connection *z_conn;
+ union {
+ struct sockaddr sa;
+ struct sockaddr_in sin;
+ struct sockaddr_in6 sin6;
+ } sa;
+ const char *address = NULL;
+ socklen_t salen;
+ uint16_t port, service, local_port = 0, local_service = 0;
+ int exclusive = 0;
+
+ if (!PyArg_ParseTuple(args, "sHH|HHp",
+ &address, &port, &service,
+ &local_port, &local_service, &exclusive))
+ return NULL;
+
+ memset(&sa, 0, sizeof(sa));
+ if (inet_pton(AF_INET, address, &sa.sin.sin_addr)) {
+ sa.sin.sin_family = AF_INET;
+ sa.sin.sin_port = htons(port);
+ salen = sizeof(sa.sin);
+ } else if (inet_pton(AF_INET6, address, &sa.sin.sin_addr)) {
+ sa.sin6.sin6_family = AF_INET6;
+ sa.sin6.sin6_port = htons(port);
+ salen = sizeof(sa.sin6);
+ } else {
+ return PyExc_TypeError;
+ }
+
+ obj = (struct py_rx_connection *)_PyObject_New(&py_rx_connectionType);
+ if (!obj)
+ return PyExc_MemoryError;
+ py_rx_connection_init((PyObject *)obj, NULL, NULL);
+ assert(obj->x == NULL);
+
+ z_conn = rx_new_connection(&sa.sa, salen, service,
+ local_port, local_service, exclusive);
+ if (!z_conn) {
+ Py_DECREF(obj);
+ return errno == ENOMEM ? PyExc_MemoryError :
+ PyErr_SetFromErrno(PyExc_IOError);
+ }
+ obj->x = z_conn;
+ return (PyObject *)obj;
+}
--- /dev/null
+/* rxgen Python wrapping support declarations
+ *
+ * Copyright (C) 2014 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#ifndef _PY_RXGEN_H
+#define _PY_RXGEN_H
+
+struct py_rx_connection {
+ PyObject_HEAD
+ struct rx_connection *x;
+};
+
+extern PyTypeObject py_rx_connectionType;
+
+extern PyObject *kafs_py_rx_new_connection(PyObject *, PyObject *);
+
+extern PyObject *py_rxgen_get_string(const void *_p, size_t n);
+extern PyObject *py_rxgen_get_uint8(const void *_p, size_t n);
+extern PyObject *py_rxgen_get_uint16(const void *_p, size_t n);
+extern PyObject *py_rxgen_get_uint32(const void *_p, size_t n);
+
+extern int py_rxgen_set_string(void *_p, size_t n, PyObject *val);
+extern int py_rxgen_set_uint8(void *_p, size_t n, PyObject *val);
+extern int py_rxgen_set_uint16(void *_p, size_t n, PyObject *val);
+extern int py_rxgen_set_uint32(void *_p, size_t n, PyObject *val);
+
+#endif /* _PY_RXGEN_H */
--- /dev/null
+/* RxRPC autogen defs
+ *
+ * Copyright (C) 2014 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#ifndef _RXGEN_H
+#define _RXGEN_H
+
+#include "af_rxrpc.h"
+
+typedef uint32_t net_xdr_t;
+
+struct rx_connection {
+ struct sockaddr_rxrpc peer;
+ uint32_t last_abort_code;
+ int fd;
+};
+
+struct rx_call {
+ int got_eor;
+ int error_code;
+ uint32_t abort_code;
+ unsigned reply_buf_size;
+ unsigned reply_size;
+ net_xdr_t *reply;
+};
+
+extern struct rx_connection *rx_new_connection(const struct sockaddr *sa,
+ socklen_t salen,
+ uint16_t service,
+ uint16_t local_port,
+ uint16_t local_service,
+ int exclusive);
+
+extern void rx_close_connection(struct rx_connection *z_conn);
+
+extern int rxrpc_send_request(struct rx_connection *z_conn,
+ struct rx_call *call,
+ const net_xdr_t *request,
+ size_t request_size,
+ size_t reply_buf_size);
+
+extern int rxrpc_wait_for_sync_reply(struct rx_connection *z_conn,
+ struct rx_call *call);
+
+#endif /* _RXGEN_H */
--- /dev/null
+#!/usr/bin/perl -w
+#
+# Tool for processing an RxRPC-based RPC API definition in a C header file to
+# produce (un)marshalling code and RPC functions to implement that API.
+#
+# It also produces a python module containing wrappers for the types, RPC
+# functions and constants in the API definition.
+#
+#
+# Copyright (C) 2014 Red Hat, Inc. All Rights Reserved.
+# Written by David Howells (dhowells@redhat.com)
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public Licence
+# as published by the Free Software Foundation; either version
+# 2 of the Licence, or (at your option) any later version.
+#
+
+use strict;
+
+sub emit_py_struct_wrapper($@);
+sub emit_py_func_simple_sync_call($$$@);
+
+die "Need list of sources\n" if ($#ARGV < 0);
+
+my @structs = (); # Structure definitions
+my %struct_sizes = (); # Structure sizes
+my %funcs = (); # Function declarations
+my %constants = (); # #defined constants
+
+#
+# Divide the lines from the files up into typed collections
+#
+my @comment = ();
+my $banner = 0;
+my $struct = 0;
+my $size = 0;
+my $func = 0;
+my @members;
+
+my @files = @ARGV;
+
+while (my $line = <>) {
+ # Gather comments for later attachment to subsequent structs and funcs
+ if ($line =~ m@^/[*]@) {
+ die if $banner;
+ $banner = 1;
+ @comment = ( $line );
+ next;
+ }
+ if ($banner) {
+ die if ($line !~ /^ [*]/);
+ push @comment, $line;
+ $banner = 0 if ($line =~ m!^ [*]/!);
+ next;
+ }
+
+ # Extract #defines
+ if ($line =~ /^#define\s+([A-Za-z0-9_]+)\s+(.*)/) {
+ $constants{$1} = $2 if ($2);
+ @comment = ();
+ next;
+ }
+
+ # Extract structures
+ if ($line =~ /^struct ([a-zA-Z_][a-zA-Z0-9_]*) {/) {
+ $struct = "$1";
+ $size = 0;
+ @members = ( [ @comment ] );
+ next;
+ }
+
+ if ($line =~ /};/ && $struct) {
+ push @structs, [ $struct, @members ];
+ $struct_sizes{$struct} = $size;
+ @members = ();
+ $struct = 0;
+ next;
+ }
+
+ # Extract structure members
+ if ($struct) {
+ # Strip trailing comments
+ $line =~ s@\s*/[*][^*]*[*]/$@@;
+
+ if ($line =~ /(uint[0-9]+_t|char)\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*;/) {
+ push @members, [ $1, $2, -1, -1 ];
+ $size += 4;
+ #print "nonarray $2\n";
+ } elsif ($line =~ /(uint[0-9]+_t|char)\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*\[\s*([^]]+)\s*\]\s*;/) {
+ die "No constant for $1" unless exists $constants{$3};
+ my $array_size = $constants{$3};
+ push @members, [ $1, $2, $array_size, -1 ];
+ $size += $array_size * 4;
+ #print "array $2\n";
+ } else {
+ die "Unrecognised struct member";
+ }
+ }
+
+ # Extract functions
+ if ($line =~ /^extern\s+int\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*\(\s*\n/) {
+ #print "func $1\n";
+ $func = "$1";
+ @members = ( [ @comment ] );
+ next;
+ }
+
+ # Extract function parameters
+ if ($func) {
+ my $dir = "";
+ my $const = "";
+ my $type = "";
+ my $name = "";
+ my $term = 0;
+ my $max = -1;
+
+ chomp $line;
+
+ $dir = $1 if ($line =~ s@/[*](IN|OUT|INOUT)[*]/\s+@@);
+
+ if ($line =~ s@/[*]\s+Max ([0-9]+)\s+[*]/@@) {
+ $max = $1;
+ } elsif ($line =~ s@/[*]\s+Max ([a-zA-Z0-9_]+)\s+[*]/@@) {
+ die "No constant for $1" unless exists $constants{$1};
+ $max = $constants{$1};
+ }
+
+ # Strip trailing comments
+ $line =~ s@\s*/[*][^*]*[*]/$@@;
+ $line =~ s/\s+$//;
+
+ if ($line =~ s/[)];$//) {
+ $term = 1;
+ } elsif ($line =~ s/,$//) {
+ $term = 0;
+ } else {
+ die "Unexpected line termination '$line'";
+ }
+
+ $line =~ s/\s+$//;
+
+ $const = $1 if ($line =~ s@^const\s+@@);
+
+ #print "\"", $line, "\"\n";
+
+ if ($line =~ /(struct\s+[a-zA-Z_][a-zA-Z0-9_]*|uint[0-9]+_t|char)\s+([*]*)([a-zA-Z_][a-zA-Z0-9_]*)\s*/) {
+ $type = $1 . $2;
+ $name = $3;
+
+ #print "- ", $name, " ISA ", $type, " ", $dir, "\n";
+ push @members, [ $type, $name, -1, $max, $dir ] unless ($1 eq "struct rx_connection");
+ } else {
+ die "Unhandled RPC call parameter";
+ }
+
+ if ($term) {
+ $funcs{$func} = [ @members ];
+ @members = ();
+ $func = 0;
+ next;
+ }
+ }
+
+}
+
+#print "----------------------------------------\n";
+
+#foreach $_ (sort keys %constants) {
+# print $_, " -> (", $constants{$_}, ")\n";
+#}
+
+# foreach $_ (keys %structs) {
+# print "-- ", $_, " --\n";
+# my $p = $structs{$_};
+# print $#{$p}, "\n";
+# my @members = @{$p};
+# shift @members;
+# foreach my $m (@members) {
+# if (@{$m}[2] eq -1) {
+# print @{$m}[1], " IS ", @{$m}[0], "\n";
+# } else {
+# print @{$m}[1], " IS ", @{$m}[0], "[", @{$m}[2], "]\n";
+# }
+# }
+# }
+
+# foreach $_ (keys %funcs) {
+# print "-- ", $_, " --\n";
+# my $p = $funcs{$_};
+# my @members = @{$p};
+# shift @members;
+# foreach my $m (@members) {
+# if (@{$m}[2] eq -1) {
+# print "\t", @{$m}[1], " IS ", @{$m}[0], "\n";
+# } else {
+# print "\t", @{$m}[1], " IS ", @{$m}[0], "[", @{$m}[2], "]\n";
+# }
+# }
+# }
+
+open RXOUT, ">afs_xg.c" || die "afs_xg.c";
+print RXOUT "/* AUTOGENERATED */\n";
+print RXOUT "#define _XOPEN_SOURCE\n";
+print RXOUT "#include <stdint.h>\n";
+print RXOUT "#include <stdio.h>\n";
+print RXOUT "#include <stdlib.h>\n";
+print RXOUT "#include <string.h>\n";
+print RXOUT "#include <unistd.h>\n";
+print RXOUT "#include <errno.h>\n";
+print RXOUT "#include <sys/socket.h>\n";
+print RXOUT "#include <arpa/inet.h>\n";
+print RXOUT "#include \"rxgen.h\"\n";
+
+foreach $_ (@files) {
+ print RXOUT "#include \"$_\"\n";
+}
+
+open PYHDR, ">afs_py.h" || die "afs_py.h";
+print PYHDR "/* AUTOGENERATED */\n";
+print PYHDR "#include <Python.h>\n";
+print PYHDR "#include <stdint.h>\n";
+print PYHDR "#include \"afs_xg.h\"\n";
+
+open PYOUT, ">afs_py.c" || die "afs_py.c";
+print PYOUT "/* AUTOGENERATED */\n";
+print PYOUT "#include <Python.h>\n";
+print PYOUT "#include \"structmember.h\"\n";
+print PYOUT "#include \"afs_py.h\"\n";
+print PYOUT "#include <arpa/inet.h>\n";
+print PYOUT "#include \"py_rxgen.h\"\n";
+
+###############################################################################
+#
+# Emit structure encoders and decoders
+#
+###############################################################################
+sub emit_struct_encdec ($@) {
+ my ($struct, @members) = @_;
+
+ # Dump the banner comment block
+ print RXOUT "\n";
+ print RXOUT @{shift @members};
+
+ # Write an encoding function
+ print RXOUT "static net_xdr_t *rxgen_encode_", $struct, "(net_xdr_t *xdr, const struct $struct *p)\n";
+ print RXOUT "{\n";
+
+ foreach my $m (@members) {
+ my ($type, $name, $array_size, $max_size) = @{$m};
+ if ($array_size != -1) {
+ print RXOUT "\tint i;\n\n";
+ last;
+ }
+ }
+
+ foreach my $m (@members) {
+ my ($type, $name, $array_size, $max_size) = @{$m};
+ if ($array_size != -1) {
+ print RXOUT "\tfor (i = 0; i < ", $array_size, "; i++)\n";
+ print RXOUT "\t\t*xdr++ = htonl(p->", $name, "[i]);\n";
+ } else {
+ print RXOUT "\t*xdr++ = htonl(p->", $name, ");\n";
+ }
+ }
+
+ print RXOUT "\treturn xdr;\n";
+ print RXOUT "}\n";
+ print RXOUT "\n";
+
+ # Write a decoding function
+ print RXOUT "static const net_xdr_t *rxgen_decode_", $struct, "(struct $struct *p, const net_xdr_t *xdr)\n";
+ print RXOUT "{\n";
+
+ foreach my $m (@members) {
+ my ($type, $name, $array_size, $max_size) = @{$m};
+ if ($array_size != -1) {
+ print RXOUT "\tint i;\n\n";
+ last;
+ }
+ }
+
+ foreach my $m (@members) {
+ my ($type, $name, $array_size, $max_size) = @{$m};
+ if ($array_size != -1) {
+ print RXOUT "\tfor (i = 0; i < ", $array_size, "; i++)\n";
+ print RXOUT "\t\tp->", $name, "[i] = ntohl(*xdr++);\n";
+ } else {
+ print RXOUT "\tp->", $name, " = ntohl(*xdr++);\n";
+ }
+ }
+
+ print RXOUT "\treturn xdr;\n";
+ print RXOUT "}\n";
+ print RXOUT "\n";
+ print RXOUT "/* XDR size ", $struct_sizes{$struct}, " */\n"
+}
+
+foreach my $s (@structs) {
+ my @members = @{$s};
+ my $struct = shift @members;
+ emit_struct_encdec($struct, @members);
+ emit_py_struct_wrapper($struct, @members);
+}
+
+###############################################################################
+#
+# Emit a function to encode a request
+#
+###############################################################################
+sub emit_func_enc_request($$$$$@)
+{
+ my ($func, $op, $request_size, $req_has_charptr, $reply_size, @request) = @_;
+
+ # Function definition and arguments
+ print RXOUT "int rxgen_send_request_", $func, "(\n";
+ print RXOUT "\tstruct rx_connection *z_conn,\n";
+ print RXOUT "\tstruct rx_call *call";
+ foreach my $p (@request) {
+ my ($type, $name, $array_size, $max_size, $dir) = @{$p};
+ print RXOUT ",\n\t";
+ print RXOUT "const " if ($type =~ "[*]");
+ print RXOUT "$type $name";
+ die "Array arg not supported '$name'" if ($array_size != -1);
+ }
+ print RXOUT ")\n";
+
+ # Function body, beginning with local variables
+ print RXOUT "{\n";
+ if ($req_has_charptr) {
+ print RXOUT "\tnet_xdr_t request[", $request_size / 4, " + 1], *xdr;\n";
+ print RXOUT "\tuint32_t tmp;\n";
+ } else {
+ print RXOUT "\tnet_xdr_t request[", $request_size / 4, "], *xdr;\n";
+ }
+
+ # Marshal the data
+ print RXOUT "\n";
+ print RXOUT "\txdr = request;\n";
+ print RXOUT "\t*xdr++ = htonl($op);\n";
+ foreach my $p (@request) {
+ my ($type, $name, $array_size, $max_size, $dir) = @{$p};
+ if ($type eq "uint8_t" ||
+ $type eq "uint16_t" ||
+ $type eq "uint32_t") {
+ print RXOUT "\t*xdr++ = htonl($name);\n";
+ } elsif ($type eq "char*") {
+ print RXOUT "\ttmp = strlen($name);\n";
+ print RXOUT "\t*xdr++ = htonl(tmp);\n";
+ print RXOUT "\txdr[tmp / 4] = 0;\n";
+ print RXOUT "\tmemcpy(xdr, $name, tmp);\n";
+ print RXOUT "\txdr += (tmp + 3) / 4;\n";
+ } elsif ($type =~ /struct ([a-zA-Z_][a-zA-Z0-9_]*)[*]/) {
+ print RXOUT "\txdr = rxgen_encode_$1(xdr, $name);\n";
+ }
+ }
+
+ # Send the message
+ print RXOUT "\n";
+ print RXOUT "\treturn rxrpc_send_request(z_conn, call, request, (xdr - request) * 4, $reply_size);\n";
+ print RXOUT "}\n";
+}
+
+###############################################################################
+#
+# Emit a function to decode a reply
+#
+###############################################################################
+sub emit_func_dec_reply($$$@)
+{
+ my ($func, $op, $reply_size, @reply) = @_;
+
+ print RXOUT "\n";
+
+ # Function definition and arguments
+ print RXOUT "void rxgen_decode_reply_", $func, "(\n";
+ print RXOUT "\tstruct rx_connection *z_conn,\n";
+ print RXOUT "\tstruct rx_call *call";
+ foreach my $p (@reply) {
+ my ($type, $name, $array_size, $max_size, $dir) = @{$p};
+ print RXOUT ",\n\t";
+ print RXOUT "$type $name";
+ die "Array arg not supported '$name'" if ($array_size != -1);
+ }
+ print RXOUT ")\n";
+
+ # Function body, beginning with local variables
+ print RXOUT "{\n";
+ print RXOUT "\tconst net_xdr_t *xdr;\n";
+
+ # Unmarshal the data
+ print RXOUT "\n";
+ print RXOUT "\txdr = call->reply;\n";
+ foreach my $p (@reply) {
+ my ($type, $name, $array_size, $max_size, $dir) = @{$p};
+ if ($type eq "int8_t*" ||
+ $type eq "int16_t*" ||
+ $type eq "int32_t*" ||
+ $type eq "uint8_t*" ||
+ $type eq "uint16_t*" ||
+ $type eq "uint32_t*") {
+ print RXOUT "\t*$name = ntohl(*xdr++);\n";
+ } elsif ($type eq "int64_t*" ||
+ $type eq "uint64_t*") {
+ print RXOUT "\t*$name = (uint64_t)ntohl(*xdr++) << 32\n";
+ print RXOUT "\t\t | (uint64_t)ntohl(*xdr++);\n";
+ } elsif ($type =~ /struct ([a-zA-Z_][a-zA-Z0-9_]*)[*]/) {
+ print RXOUT "\txdr = rxgen_decode_$1($name, xdr);\n";
+ }
+ }
+
+ print RXOUT "}\n";
+}
+
+###############################################################################
+#
+# Emit a function to make a simple synchronous call
+#
+###############################################################################
+sub emit_func_simple_sync_call($$$$$@)
+{
+ my ($func, $request_size, $reply_size, $_request, $_reply, @params) = @_;
+ my @request = @{$_request};
+ my @reply = @{$_reply};
+
+ print RXOUT "\n";
+
+ # Function definition and arguments
+ print RXOUT "int ", $func, "(\n";
+ print RXOUT "\tstruct rx_connection *z_conn";
+ foreach my $p (@params) {
+ my ($type, $name, $array_size, $max_size, $dir) = @{$p};
+ print RXOUT ",\n\t";
+ print RXOUT "const " if ($type =~ "[*]" && $dir eq "IN");
+ print RXOUT "$type $name";
+ die "Array arg not supported '$name'" if ($array_size != -1);
+ }
+ print RXOUT ")\n";
+
+ # Function body, beginning with local variables
+ print RXOUT "{\n";
+ print RXOUT "\tstruct rx_call *call;\n";
+ print RXOUT "\tint ret;\n";
+ print RXOUT "\n";
+
+ # Allocate a call record and reply buffer
+ print RXOUT "\tcall = malloc(sizeof(*call) + $reply_size);\n";
+ print RXOUT "\tif (!call)\n";
+ print RXOUT "\t\treturn -1;\n";
+ print RXOUT "\tcall->reply = (void *)call + sizeof(*call);\n";
+ print RXOUT "\n";
+
+ # Send the request
+ print RXOUT "\tret = rxgen_send_request_", $func, "(";
+ print RXOUT "z_conn, call";
+ foreach my $p (@request) {
+ my ($type, $name, $array_size, $max_size, $dir) = @{$p};
+ print RXOUT ", $name";
+ }
+ print RXOUT ");\n";
+ print RXOUT "\tif (ret != 0) {\n";
+ print RXOUT "\t\tfree(call);\n";
+ print RXOUT "\t\treturn ret;\n";
+ print RXOUT "\t}\n";
+
+ # Wait for the reply
+ print RXOUT "\tret = rxrpc_wait_for_sync_reply(z_conn, call);\n";
+ print RXOUT "\tif (ret != 0) {\n";
+ print RXOUT "\t\tfree(call);\n";
+ print RXOUT "\t\treturn ret;\n";
+ print RXOUT "\t}\n";
+ print RXOUT "\n";
+
+ # Unmarshal the reply
+ if (@reply) {
+ print RXOUT "\trxgen_decode_reply_", $func, "(";
+ print RXOUT "z_conn, call";
+ foreach my $p (@reply) {
+ my ($type, $name, $array_size, $max_size, $dir) = @{$p};
+ print RXOUT ", $name";
+ }
+ print RXOUT ");\n";
+ }
+
+ print RXOUT "\n";
+ print RXOUT "\treturn 0;\n";
+ print RXOUT "}\n";
+}
+
+###############################################################################
+#
+# Dump RPC call encoders and decoders
+#
+###############################################################################
+foreach $func (sort keys %funcs) {
+ my @params = @{$funcs{$func}};
+
+ # Dump the banner comment block
+ my @banner = @{shift @params};
+ print RXOUT "\n";
+ print RXOUT @banner;
+ print PYOUT "\n";
+ print PYOUT @banner;
+
+ # Find the Operation ID from the banner comment
+ my $op = "";
+ foreach $_ (@banner) {
+ if ($_ =~ /Operation: ([A-Z][_A-Z0-9]+)/) {
+ $op = $1;
+ last;
+ }
+ }
+
+ die "Operation ID unspecified for $func" unless $op;
+
+ # Filter the parameters into request and reply
+ my @request = ();
+ my @reply = ();
+ my $request_size = 4;
+ my $reply_size = 0;
+ my $req_has_charptr = 0;
+
+ foreach my $p (@params) {
+ my ($type, $name, $array_size, $max_size, $dir) = @{$p};
+
+ #print RXOUT $dir, " ", $type, " ", $name, "\n";
+
+ my $size = 0;
+ my $has_charptr = 0;
+ if ($array_size == -1) {
+ if ($type eq "char*") {
+ $size = (4 + $max_size + 3) & ~3;
+ $has_charptr = 1;
+ } elsif ($type eq "uint8_t" || $type eq "uint8_t*" ||
+ $type eq "uint16_t" || $type eq "uint16_t*" ||
+ $type eq "uint32_t" || $type eq "uint32_t*" ||
+ $type eq "uint64_t" || $type eq "uint64_t*") {
+ $size = 4;
+ } elsif ($type =~ /struct ([a-zA-Z_][a-zA-Z0-9_]*)[*]/) {
+ die "No encoding for $type" unless exists $struct_sizes{$1};
+ $size = $struct_sizes{$1};
+ } else {
+ die "Unsupported type \"$type\"";
+ }
+ } else {
+ if ($type eq "uint8_t" || $type eq "uint8_t*" ||
+ $type eq "uint16_t" || $type eq "uint16_t*" ||
+ $type eq "uint32_t" || $type eq "uint32_t*" ||
+ $type eq "uint64_t" || $type eq "uint64_t*") {
+ $size = 4;
+ } else {
+ die "Unsupported type \"$type\"";
+ }
+ $size *= $array_size;
+ }
+
+ if ($dir eq "IN") {
+ push @request, $p;
+ $request_size += $size;
+ $req_has_charptr |= $has_charptr;
+ } elsif ($dir eq "OUT") {
+ push @reply, $p;
+ $reply_size += $size;
+ } elsif ($dir eq "INOUT") {
+ push @reply, $p;
+ push @request, $p;
+ $request_size += $size;
+ $reply_size += $size;
+ $req_has_charptr |= $has_charptr;
+ }
+ }
+
+ #print RXOUT "/* req max size: $request_size */\n";
+ #print RXOUT "/* rep max size: $reply_size */\n";
+ emit_func_enc_request($func, $op, $request_size, $req_has_charptr, $reply_size, @request);
+ emit_func_dec_reply($func, $op, $reply_size, @reply)
+ if (@reply);
+ emit_func_simple_sync_call($func, $request_size, $reply_size, \@request, \@reply, @params);
+ emit_py_func_simple_sync_call($func, \@request, \@reply, @params);
+}
+
+###############################################################################
+#
+# Emit python structure wrappers
+#
+###############################################################################
+sub emit_py_struct_wrapper($@) {
+ my ($struct, @members) = @_;
+
+ # Dump the banner comment block
+ my @comments = @{shift @members};
+
+ print PYHDR "\n";
+ print PYHDR @comments;
+ print PYOUT "\n";
+ print PYOUT @comments;
+
+ # Write a python wrapper struct
+ print PYHDR "struct py_$struct {\n";
+ print PYHDR "\tPyObject_HEAD\n";
+ print PYHDR "\tstruct $struct x;\n";
+ print PYHDR "};\n";
+ print PYHDR "\n";
+
+ # We want allocation and deallocation functions
+ print PYOUT "static PyObject *\n";
+ print PYOUT "py_", $struct, "_new(PyTypeObject *type, PyObject *args, PyObject *kwds)\n";
+ print PYOUT "{\n";
+ print PYOUT "\treturn (PyObject *)(struct py_$struct *)type->tp_alloc(type, 0);\n";
+ print PYOUT "}\n";
+ print PYOUT "\n";
+ print PYOUT "static void\n";
+ print PYOUT "py_", $struct, "_dealloc(struct py_$struct *self)\n";
+ print PYOUT "{\n";
+ print PYOUT "\tPy_TYPE(self)->tp_free((PyObject *)self);\n";
+ print PYOUT "}\n";
+ print PYOUT "\n";
+
+ # Divide into single members and array members
+ my @singles = ();
+ my @arrays = ();
+ foreach my $m (@members) {
+ my ($type, $name, $array_size, $max_size) = @{$m};
+ if ($array_size == -1) {
+ push @singles, $m;
+ } else {
+ push @arrays, $m;
+ }
+ }
+
+ # Any non-array elements are made directly accessible to the Python interpreter
+ print PYOUT "static PyMemberDef py_", $struct, "_members[] = {\n";
+ if (@singles) {
+ foreach my $m (@singles) {
+ my ($type, $name, $array_size, $max_size) = @{$m};
+ print PYOUT "\t{ \"$name\", ";
+ if ($type eq "char") {
+ print PYOUT "T_CHAR";
+ } elsif ($type eq "int8_t") {
+ print PYOUT "T_BYTE";
+ } elsif ($type eq "int16_t") {
+ print PYOUT "T_SHORT";
+ } elsif ($type eq "int32_t") {
+ print PYOUT "T_INT";
+ } elsif ($type eq "int64_t") {
+ print PYOUT "T_LONGLONG";
+ } elsif ($type eq "uint8_t") {
+ print PYOUT "T_UBYTE";
+ } elsif ($type eq "uint16_t") {
+ print PYOUT "T_USHORT";
+ } elsif ($type eq "uint32_t") {
+ print PYOUT "T_UINT";
+ } elsif ($type eq "uint64_t") {
+ print PYOUT "T_ULONGLONG";
+ } elsif ($type =~ /struct ([a-zA-Z_][a-zA-Z0-9_]*)[*]/) {
+ die "Don't py-wrap structs yet";
+ } else {
+ die "Unsupported type \"$type\"";
+ }
+ print PYOUT ", offsetof(struct py_$struct, x.$name), 0, \"\"},\n";
+ }
+ }
+ print PYOUT "\t{}\n";
+ print PYOUT "};\n";
+ print PYOUT "\n";
+
+ # Array elements have to be accessed through ->tp_[sg]etattro() as
+ # tuples (int[]/uint[]) or strings (char[])
+ if (@arrays) {
+ # The attribute get function
+ print PYOUT "static PyObject *\n";
+ print PYOUT "py_", $struct, "_getattro(PyObject *_self, PyObject *name)\n";
+ print PYOUT "{\n";
+ print PYOUT "\tstruct py_$struct *self = (struct py_$struct *)_self;\n";
+ print PYOUT "\n";
+ print PYOUT "\tif (PyUnicode_Check(name)) {\n";
+
+ foreach my $m (@arrays) {
+ my ($type, $name, $array_size, $max_size) = @{$m};
+
+ print PYOUT "\t\tif (PyUnicode_CompareWithASCIIString(name, \"$name\") == 0)\n";
+ if ($type eq "char") {
+ print PYOUT "\t\t\treturn py_rxgen_get_string(&self->x.$name, $array_size);\n";
+ } elsif ($type eq "uint8_t") {
+ print PYOUT "\t\t\treturn py_rxgen_get_uint8(&self->x.$name, $array_size);\n";
+ } elsif ($type eq "uint16_t") {
+ print PYOUT "\t\t\treturn py_rxgen_get_uint16(&self->x.$name, $array_size);\n";
+ } elsif ($type eq "uint32_t") {
+ print PYOUT "\t\t\treturn py_rxgen_get_uint32(&self->x.$name, $array_size);\n";
+ } elsif ($type =~ /struct ([a-zA-Z_][a-zA-Z0-9_]*)[*]/) {
+ die "Don't py-wrap struct arrays yet";
+ } else {
+ die "Unsupported array type \"$type\"";
+ }
+ }
+
+ print PYOUT "\t}\n";
+ print PYOUT "\n";
+ print PYOUT "\treturn PyObject_GenericGetAttr(_self, name);\n";
+ print PYOUT "}\n";
+ print PYOUT "\n";
+
+ # The attribute set function
+ print PYOUT "static int\n";
+ print PYOUT "py_", $struct, "_setattro(PyObject *_self, PyObject *name, PyObject *val)\n";
+ print PYOUT "{\n";
+ print PYOUT "\tstruct py_$struct *self = (struct py_$struct *)_self;\n";
+ print PYOUT "\n";
+ print PYOUT "\tif (PyUnicode_Check(name)) {\n";
+
+ foreach my $m (@arrays) {
+ my ($type, $name, $array_size, $max_size) = @{$m};
+
+ print PYOUT "\t\tif (PyUnicode_CompareWithASCIIString(name, \"$name\") == 0)\n";
+ if ($type eq "char") {
+ print PYOUT "\t\t\treturn py_rxgen_set_string(&self->x.$name, $array_size, val);\n";
+ } elsif ($type eq "uint8_t") {
+ print PYOUT "\t\t\treturn py_rxgen_set_uint8(&self->x.$name, $array_size, val);\n";
+ } elsif ($type eq "uint16_t") {
+ print PYOUT "\t\t\treturn py_rxgen_set_uint16(&self->x.$name, $array_size, val);\n";
+ } elsif ($type eq "uint32_t") {
+ print PYOUT "\t\t\treturn py_rxgen_set_uint32(&self->x.$name, $array_size, val);\n";
+ } elsif ($type =~ /struct ([a-zA-Z_][a-zA-Z0-9_]*)[*]/) {
+ die "Don't py-wrap struct arrays yet";
+ } else {
+ die "Unsupported array type \"$type\"";
+ }
+ }
+
+ print PYOUT "\t}\n";
+ print PYOUT "\n";
+ print PYOUT "\treturn PyObject_GenericSetAttr(_self, name, val);\n";
+ print PYOUT "}\n";
+ print PYOUT "\n";
+ }
+
+ # Emit the Python type definition
+ print PYOUT "static PyTypeObject py_", $struct, "Type = {\n";
+
+ print PYOUT "\tPyVarObject_HEAD_INIT(NULL, 0)\n";
+ print PYOUT "\t\"kafs.$struct\",\t\t/*tp_name*/\n";
+ print PYOUT "\tsizeof(struct py_$struct),\t/*tp_basicsize*/\n";
+ print PYOUT "\t0,\t\t\t\t/*tp_itemsize*/\n";
+ print PYOUT "\t(destructor)py_", $struct, "_dealloc, /*tp_dealloc*/\n";
+ print PYOUT "\t0,\t\t\t\t/*tp_print*/\n";
+ print PYOUT "\t0,\t\t\t\t/*tp_getattr*/\n";
+ print PYOUT "\t0,\t\t\t\t/*tp_setattr*/\n";
+ print PYOUT "\t0,\t\t\t\t/*tp_compare*/\n";
+ print PYOUT "\t0,\t\t\t\t/*tp_repr*/\n";
+ print PYOUT "\t0,\t\t\t\t/*tp_as_number*/\n";
+ print PYOUT "\t0,\t\t\t\t/*tp_as_sequence*/\n";
+ print PYOUT "\t0,\t\t\t\t/*tp_as_mapping*/\n";
+ print PYOUT "\t0,\t\t\t\t/*tp_hash */\n";
+ print PYOUT "\t0,\t\t\t\t/*tp_call*/\n";
+ print PYOUT "\t0,\t\t\t\t/*tp_str*/\n";
+ if (@arrays) {
+ print PYOUT "\tpy_", $struct, "_getattro,\n";
+ print PYOUT "\tpy_", $struct, "_setattro,\n";
+ } else {
+ print PYOUT "\t0,\t\t\t\t/*tp_getattro*/\n";
+ print PYOUT "\t0,\t\t\t\t/*tp_setattro*/\n";
+ }
+ print PYOUT "\t0,\t\t\t\t/*tp_as_buffer*/\n";
+ print PYOUT "\tPy_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/\n";
+ if (@comments) {
+ print PYOUT "\t";
+ foreach my $c (@comments) {
+ $c =~ s/\s+$//;
+ $c =~ s/^\s+//;
+ next if ($c eq "/*" || $c eq "*/");
+ $c =~ s/^[*] //;
+ $c =~ s/^[*]$//;
+ print PYOUT "\n\t\t\"", $c, "\\n\"";
+ }
+ print PYOUT ",\n";
+ } else {
+ print PYOUT "\t\"\",\t\t\t/* tp_doc */\n";
+ }
+ print PYOUT "\t0,\t\t\t\t/* tp_traverse */\n";
+ print PYOUT "\t0,\t\t\t\t/* tp_clear */\n";
+ print PYOUT "\t0,\t\t\t\t/* tp_richcompare */\n";
+ print PYOUT "\t0,\t\t\t\t/* tp_weaklistoffset */\n";
+ print PYOUT "\t0,\t\t\t\t/* tp_iter */\n";
+ print PYOUT "\t0,\t\t\t\t/* tp_iternext */\n";
+ print PYOUT "\t0,\t\t\t\t/* tp_methods */\n";
+ if (@singles) {
+ print PYOUT "\tpy_", $struct, "_members,\n";
+ } else {
+ print PYOUT "\t0,\t\t\t/* tp_members */\n";
+ }
+ print PYOUT "\t0,\t\t\t\t/* tp_getset */\n";
+ print PYOUT "\t0,\t\t\t\t/* tp_base */\n";
+ print PYOUT "\t0,\t\t\t\t/* tp_dict */\n";
+ print PYOUT "\t0,\t\t\t\t/* tp_descr_get */\n";
+ print PYOUT "\t0,\t\t\t\t/* tp_descr_set */\n";
+ print PYOUT "\t0,\t\t\t\t/* tp_dictoffset */\n";
+ print PYOUT "\t0,\t\t\t\t/* tp_init */\n";
+ print PYOUT "\t0,\t\t\t\t/* tp_alloc */\n";
+ print PYOUT "\tpy_", $struct, "_new,\t/* tp_new */\n";
+ print PYOUT "};\n";
+
+ # Emit a function to allocate such a type
+ print PYHDR "extern PyObject *kafs_new_py_$struct(PyObject *, PyObject *);\n";
+
+ print PYOUT "\n";
+ print PYOUT "PyObject *\n";
+ print PYOUT "kafs_new_py_$struct(PyObject *_self, PyObject *args)\n";
+ print PYOUT "{\n";
+ print PYOUT "\tPyObject *obj;\n";
+ print PYOUT "\tobj = _PyObject_New(&py_", $struct, "Type);\n";
+ print PYOUT "\treturn obj ?: PyExc_MemoryError;\n";
+ print PYOUT "}\n";
+}
+
+###############################################################################
+#
+# Emit python structure wrapper static method table
+#
+###############################################################################
+
+print PYOUT "\n";
+print PYOUT "/*\n";
+print PYOUT " * The static methods.\n";
+print PYOUT " */\n";
+print PYOUT "static PyMethodDef module_methods[] = {\n";
+
+foreach my $s (@structs) {
+ my $struct = @{$s}[0];
+ print PYOUT "\t{\"new_$struct\", (PyCFunction)kafs_new_py_$struct, METH_NOARGS,\n";
+ print PYOUT "\t \"Create a new $struct record.\"\n";
+ print PYOUT "\t},\n";
+}
+
+foreach $func (sort keys %funcs) {
+ my @params = @{$funcs{$func}};
+ print PYOUT "\t{\"$func\", (PyCFunction)kafs_$func, METH_VARARGS, \"\" },\n";
+}
+
+print PYOUT "\t{\"rx_new_connection\", (PyCFunction)kafs_py_rx_new_connection, METH_VARARGS,\n";
+print PYOUT "\t\"\" },\n";
+
+print PYOUT "\t{}\n";
+print PYOUT "};\n";
+
+###############################################################################
+#
+# Emit python structure wrapper loader
+#
+###############################################################################
+print PYOUT "\n";
+
+print PYOUT "static PyModuleDef kafs_module = {\n";
+print PYOUT "\t.m_base = PyModuleDef_HEAD_INIT,\n";
+print PYOUT "\t.m_name = \"kafs\",\n";
+print PYOUT "\t.m_doc = \"AFS stuff.\",\n";
+print PYOUT "\t.m_size = -1,\n";
+print PYOUT "\t.m_methods = module_methods,\n";
+print PYOUT "};\n";
+
+print PYHDR "\n";
+print PYHDR "extern PyObject *pykafs_load_wrappers(void);\n";
+
+print PYOUT "\n";
+print PYOUT "PyObject *pykafs_load_wrappers(void)\n";
+print PYOUT "{\n";
+print PYOUT "\tPyObject *m;\n";
+
+# Load types
+if (@structs) {
+ print PYOUT "\tif (";
+ print PYOUT "PyType_Ready(&py_rx_connectionType) < 0";
+ my $first = 0;
+ foreach my $s (@structs) {
+ my @members = @{$s};
+ my $struct = $members[0];
+ print PYOUT " ||\n\t " unless ($first);
+ print PYOUT "PyType_Ready(&py_", $struct, "Type) < 0";
+ $first = 0;
+ }
+ print PYOUT ")\n";
+ print PYOUT "\t\treturn NULL;\n";
+}
+
+print PYOUT "\n";
+print PYOUT "\tm = PyModule_Create(&kafs_module);\n";
+print PYOUT "\tif (!m)\n";
+print PYOUT "\t\treturn NULL;\n";
+
+if (%constants) {
+ print PYOUT "\n";
+ foreach my $c (sort keys %constants) {
+ print PYOUT "\tPyModule_AddIntConstant(m, \"$c\", $c);\n";
+ }
+}
+
+if (@structs) {
+ print PYOUT "\n";
+ foreach my $s (@structs) {
+ my @members = @{$s};
+ my $struct = $members[0];
+ print PYOUT "\tPy_INCREF(&py_", $struct, "Type);\n";
+ print PYOUT "\tPyModule_AddObject(m, \"$struct\", (PyObject *)&py_", $struct, "Type);\n";
+ }
+
+ print PYOUT "\n";
+ print PYOUT "\treturn m;\n";
+}
+
+print PYOUT "}\n";
+
+###############################################################################
+#
+# Emit a python wrapper function to make a simple synchronous call
+#
+###############################################################################
+sub emit_py_func_simple_sync_call($$$@)
+{
+ my ($func, $_request, $_reply, @params) = @_;
+ my @request = @{$_request};
+ my @reply = @{$_reply};
+
+ print PYOUT "PyObject *\n";
+ print PYOUT "kafs_", $func, "(PyObject *_self, PyObject *args)\n";
+ print PYOUT "{\n";
+
+ # Declare parameter variables
+ my $need_tmp = 0;
+ print PYOUT "\tstruct py_rx_connection *z_conn;\n";
+ foreach my $p (@params) {
+ my ($type, $name, $array_size, $max_size, $dir) = @{$p};
+ if ($type eq "char*") {
+ die "String output args not supported" unless ($dir eq "IN");
+ print PYOUT "\tconst char *param_$name;\n";
+ } elsif ($type eq "int8_t" || $type eq "int8_t*" ||
+ $type eq "int16_t" || $type eq "int16_t*" ||
+ $type eq "int32_t" || $type eq "int32_t*" ||
+ $type eq "int64_t" || $type eq "int64_t*" ||
+ $type eq "uint8_t" || $type eq "uint8_t*" ||
+ $type eq "uint16_t" || $type eq "uint16_t*" ||
+ $type eq "uint32_t" || $type eq "uint32_t*" ||
+ $type eq "uint64_t" || $type eq "uint64_t*"
+ ) {
+ $type =~ s/[*]$//;
+ $need_tmp = 1 unless ($dir eq "IN");
+ print PYOUT "\t$type param_$name;\n";
+ } elsif ($type =~ /struct ([a-zA-Z_][a-zA-Z0-9_]*)[*]/) {
+ die "INOUT struct args not supported" if ($dir eq "INOUT");
+ print PYOUT "\tstruct py_$1 *param_$name;\n";
+ } else {
+ die "Unsupported type \"$type\"";
+ }
+ }
+
+ # Replies are passed back into lists provided by the caller. INOUT variable
+ # input values must already occupy the lists.
+ foreach my $p (@reply) {
+ my ($type, $name, $array_size, $max_size, $dir) = @{$p};
+ print PYOUT "\tPyObject *reply_$name;\n";
+ }
+
+ print PYOUT "\tPyObject *tmp;\n" if ($need_tmp);
+ print PYOUT "\tPyObject *res = NULL;\n";
+ print PYOUT "\tint ret;\n";
+
+ # Make use of the tuple parser to extract the arguments and check their
+ # types for us.
+ print PYOUT "\n";
+ print PYOUT "\tif (!PyArg_ParseTuple(args, \"O!";
+
+ foreach my $p (@params) {
+ my ($type, $name, $array_size, $max_size, $dir) = @{$p};
+ if ($dir ne "IN") {
+ print PYOUT "O!";
+ } elsif ($type eq "char*") {
+ print PYOUT "s";
+ } elsif ($type eq "int8_t") {
+ print PYOUT "B";
+ } elsif ($type eq "int16_t") {
+ print PYOUT "h";
+ } elsif ($type eq "int32_t") {
+ print PYOUT "i";
+ } elsif ($type eq "int64_t") {
+ print PYOUT "L";
+ } elsif ($type eq "uint8_t") {
+ print PYOUT "b";
+ } elsif ($type eq "uint16_t") {
+ print PYOUT "H";
+ } elsif ($type eq "uint32_t") {
+ print PYOUT "I";
+ } elsif ($type eq "uint64_t") {
+ print PYOUT "K";
+ } elsif ($type =~ /struct ([a-zA-Z_][a-zA-Z0-9_]*)[*]/) {
+ print PYOUT "O!";
+ }
+ }
+
+ print PYOUT "\",\n";
+ print PYOUT "\t\t\t &py_rx_connectionType, &z_conn";
+
+ foreach my $p (@params) {
+ my ($type, $name, $array_size, $max_size, $dir) = @{$p};
+ print PYOUT ",\n";
+ print PYOUT "\t\t\t /*$dir*/ ";
+ if ($dir ne "IN") {
+ print PYOUT "&PyList_Type, &reply_$name";
+ } elsif ($type eq "char*" ||
+ $type eq "int8_t" || $type eq "int8_t" ||
+ $type eq "int16_t" || $type eq "int16_t" ||
+ $type eq "int32_t" || $type eq "int32_t" ||
+ $type eq "int64_t" || $type eq "int64_t" ||
+ $type eq "uint8_t" || $type eq "uint8_t" ||
+ $type eq "uint16_t" || $type eq "uint16_t" ||
+ $type eq "uint32_t" || $type eq "uint32_t" ||
+ $type eq "uint64_t" || $type eq "uint64_t") {
+ print PYOUT "¶m_$name";
+ } elsif ($type =~ /struct ([a-zA-Z_][a-zA-Z0-9_]*)[*]/) {
+ print PYOUT "&py_", $1, "Type, ¶m_$name";
+ } else {
+ die "Unsupported type \"$type\"";
+ }
+ }
+ print PYOUT "))\n";
+ print PYOUT "\t\treturn NULL;\n";
+
+ # Allocate reply buffer objects
+ if (@reply) {
+ print PYOUT "\n";
+ foreach my $p (@reply) {
+ my ($type, $name, $array_size, $max_size, $dir) = @{$p};
+ if ($type =~ /struct ([a-zA-Z_][a-zA-Z0-9_]*)[*]/) {
+ print PYOUT "\tparam_$name = (struct py_$1 *)kafs_new_py_$1(NULL, NULL);\n";
+ print PYOUT "\tif (!param_$name)\n";
+ print PYOUT "\t\tgoto error_alloc_$name;\n";
+ }
+ }
+ }
+
+ # Make the call
+ print PYOUT "\n";
+ print PYOUT "\tret = $func(\n";
+ print PYOUT "\t\tz_conn->x";
+
+ foreach my $p (@params) {
+ print PYOUT ",\n";
+ my ($type, $name, $array_size, $max_size, $dir) = @{$p};
+ if ($type eq "char*" ||
+ $type eq "int8_t" || $type eq "int8_t*" ||
+ $type eq "int16_t" || $type eq "int16_t*" ||
+ $type eq "int32_t" || $type eq "int32_t*" ||
+ $type eq "int64_t" || $type eq "int64_t*" ||
+ $type eq "uint8_t" || $type eq "uint8_t*" ||
+ $type eq "uint16_t" || $type eq "uint16_t*" ||
+ $type eq "uint32_t" || $type eq "uint32_t*" ||
+ $type eq "uint64_t" || $type eq "uint64_t*") {
+ if ($dir eq "IN") {
+ print PYOUT "\t\tparam_$name";
+ } else {
+ print PYOUT "\t\t¶m_$name";
+ }
+ } elsif ($type =~ /struct ([a-zA-Z_][a-zA-Z0-9_]*)[*]/) {
+ print PYOUT "\t\t¶m_$name->x";
+ } else {
+ die "Unsupported type \"$type\"";
+ }
+ }
+ print PYOUT ");\n";
+ print PYOUT "\tif (ret != 0) {\n";
+ print PYOUT "\t\tif (ret == -1 && errno == ENOMEM)\n";
+ print PYOUT "\t\t\tres = PyExc_MemoryError;\n";
+ print PYOUT "\t\telse if (ret == -1)\n";
+ print PYOUT "\t\t\tres = PyErr_SetFromErrno(PyExc_IOError);\n";
+ print PYOUT "\t\telse\n";
+ print PYOUT "\t\t\tres = PyLong_FromLong(ret);\n";
+ print PYOUT "\t\tgoto error;\n";
+ print PYOUT "\t}\n";
+
+ # Pass back any replies
+ if (@reply) {
+ print PYOUT "\n";
+ foreach my $p (@reply) {
+ my ($type, $name, $array_size, $max_size, $dir) = @{$p};
+
+ my $set_null = 0;
+ my $var = "tmp";
+
+ if ($type eq "int8_t" || $type eq "int8_t*" ||
+ $type eq "int16_t" || $type eq "int16_t*" ||
+ $type eq "int32_t" || $type eq "int32_t*") {
+ print PYOUT "\ttmp = PyLong_FromLong(param_$name);\n";
+ } elsif ($type eq "int64_t" || $type eq "int64_t*") {
+ print PYOUT "\ttmp = PyLong_FromLongLong(param_$name);\n";
+ } elsif ($type eq "uint8_t" || $type eq "uint8_t*" ||
+ $type eq "uint16_t" || $type eq "uint16_t*" ||
+ $type eq "uint32_t" || $type eq "uint32_t*") {
+
+ print PYOUT "\ttmp = PyLong_FromUnsignedLong(param_$name);\n";
+
+ } elsif ($type eq "uint64_t" || $type eq "uint64_t*") {
+ print PYOUT "\ttmp = PyLong_FromUnsignedLongLong(param_$name);\n";
+
+ } elsif ($type =~ /struct ([a-zA-Z_][a-zA-Z0-9_]*)[*]/) {
+ $var = "(PyObject *)param_$name";
+ $set_null = 1;
+ } else {
+ die "Unsupported type \"$type\"";
+ }
+
+ if ($var eq "tmp") {
+ print PYOUT "\tif (!tmp)\n";
+ print PYOUT "\t\tgoto error;\n";
+ }
+
+ print PYOUT "\tif (PyList_Insert(reply_$name, 0, $var) == -1)\n";
+ print PYOUT "\t\tgoto error", $need_tmp ? "_tmp" : "", ";\n";
+ print PYOUT "\tparam_$name = NULL;\n" if ($set_null);
+ }
+
+ }
+
+ # Successful return
+ print PYOUT "\n";
+ print PYOUT "\treturn PyLong_FromLong(0);\n";
+
+ # Error cleanups
+ print PYOUT "\n";
+ if ($need_tmp) {
+ print PYOUT "error_tmp:\n";
+ print PYOUT "\tPy_DECREF(tmp);\n";
+ }
+ print PYOUT "error:\n";
+ if (@reply) {
+ foreach my $p (reverse @reply) {
+ my ($type, $name, $array_size, $max_size, $dir) = @{$p};
+ if ($type =~ /struct ([a-zA-Z_][a-zA-Z0-9_]*)[*]/) {
+ print PYOUT "\tPy_XDECREF(param_$name);\n";
+ print PYOUT "error_alloc_$name:\n";
+ }
+ }
+ }
+
+ print PYOUT "\treturn res;\n";
+
+ # End the function
+ print PYOUT "}\n";
+}
--- /dev/null
+#!/usr/bin/python3
+#
+# Use VLDB record look-up-by-name to test the infrastructure.
+#
+# Copyright (C) 2014 Red Hat, Inc. All Rights Reserved.
+# Written by David Howells (dhowells@redhat.com)
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public Licence
+# as published by the Free Software Foundation; either version
+# 2 of the Licence, or (at your option) any later version.
+#
+
+import sys;
+import getopt;
+import kafs;
+import dns.resolver;
+
+cell = "grand.central.org";
+volumes = [ "root.cell" ];
+
+# The user can specify the cell and provide a list of volume names
+opts, args = getopt.getopt(sys.argv[1:], "c:", [ "cell=" ]);
+for o, a in opts:
+ if o == "-c":
+ cell = a;
+
+if args:
+ volumes = args;
+
+print("cell:", cell);
+
+# Find a list of Volume Location servers to contact
+vladdrs = [];
+try:
+ srv_list = dns.resolver.query("_afs3-vlserver._udp." + cell, "SRV");
+ for record in srv_list:
+ A_recs = dns.resolver.query(record.target, 'A');
+ for addr in A_recs:
+ vladdrs.append(addr.address);
+ print(record.target, ":", vladdrs);
+except dns.resolver.NXDOMAIN:
+ print("Couldn't find any SRV records");
+except dns.resolver.NoAnswer:
+ print("Couldn't find any SRV records");
+
+try:
+ afsdb_list = dns.resolver.query(cell, "AFSDB");
+ for record in afsdb_list:
+ A_recs = dns.resolver.query(record.hostname, 'A');
+ for addr in A_recs:
+ vladdrs.append(addr.address);
+ print(record.hostname, ":", vladdrs);
+except dns.resolver.NoAnswer:
+ print("Couldn't find any AFSDB records");
+
+if not vladdrs:
+ raise RuntimeError("Couldn't find any VL server addresses");
+
+print("vladdrs:", vladdrs);
+
+# Go through the list of VLDB servers until one answers a probe request
+for vlserver in vladdrs:
+ print("Trying", vlserver);
+
+ z_conn = kafs.rx_new_connection(vlserver, kafs.AFS_VL_PORT, kafs.VL_SERVICE);
+
+ try:
+ ret = kafs.VL_Probe(z_conn);
+ if ret == 0:
+ break;
+ except ConnectionRefusedError:
+ pass;
+ del z_conn;
+
+if not z_conn:
+ raise RuntimeError("Couldn't connect to a server");
+
+# Look up each of the volumes in the list
+for vol in volumes:
+ vldblist = [];
+ ret = kafs.VL_GetEntryByName(z_conn, vol, vldblist);
+ if ret:
+ raise RuntimeError("Abort occurred {:d}".format(ret));
+
+ for i in vldblist:
+ print("[", i.name, "]");
+ print("\tnum\t", i.nServers);
+ print("\ttype\t", i.volumeType);
+ print("\tvid\t", i.volumeId);
+ print("\tflags\t {:x}".format(i.flags));