pax_global_header00006660000000000000000000000064122365624450014523gustar00rootroot0000000000000052 comment=d8249f602091858596aecf519ae388b5fa026882 mdata-client-0.0.1/000077500000000000000000000000001223656244500140635ustar00rootroot00000000000000mdata-client-0.0.1/.gitignore000066400000000000000000000000211223656244500160440ustar00rootroot00000000000000/mdata-* /proto/ mdata-client-0.0.1/LICENSE000066400000000000000000000020701223656244500150670ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2013, Joyent, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. mdata-client-0.0.1/Makefile000066400000000000000000000050721223656244500155270ustar00rootroot00000000000000 VERSION = 0.0.1 CC = gcc CTFMERGE = /bin/true CTFCONVERT = /bin/true GNUTAR = tar PWD := $(shell pwd) UNAME_S := $(shell uname -s) PLATFORM_OK = false CFILES = dynstr.c proto.c common.c base64.c crc32.c reqid.c OBJS = $(CFILES:%.c=%.o) HDRS = dynstr.h plat.h proto.h common.h base64.h crc32.h reqid.h CFLAGS = -I$(PWD) -Wall -Wextra -Werror -g -O2 LDLIBS = BINDIR = /usr/sbin MANSECT = 1m MANDIR = /usr/share/man/man$(MANSECT) DESTDIR = $(PWD)/proto PROGS = \ mdata-get \ mdata-list \ mdata-put \ mdata-delete PROTO_PROGS = \ $(PROGS:%=$(DESTDIR)$(BINDIR)/%) PROTO_MANPAGES = \ $(PROGS:%=$(DESTDIR)$(MANDIR)/%.$(MANSECT)) INSTALL_TARGETS = \ $(PROTO_PROGS) \ $(PROTO_MANPAGES) # # Platform-specific definitions # ifeq ($(UNAME_S),SunOS) CFLAGS += -D__HAVE_BOOLEAN_T CFILES += plat/sunos.c plat/unix_common.c HDRS += plat/unix_common.h LDLIBS += -lnsl -lsocket -lsmbios PLATFORM_OK = true GNUTAR = gtar endif ifeq ($(UNAME_S),Linux) CFILES += plat/linux.c plat/unix_common.c HDRS += plat/unix_common.h PLATFORM_OK = true MANSECT = 1 INSTALL_TARGETS += $(DESTDIR)/lib/smartdc/mdata-get PKGNAME = joyent-mdata-client endif ifeq ($(PLATFORM_OK),false) $(error Unknown platform: $(UNAME_S)) endif # # Build Targets # .PHONY: all world world: all all: $(PROGS) %.o: %.c $(CC) -c $(CFLAGS) -o $@ $< $(CTFCONVERT) -l mdata-client $@ mdata-%: $(OBJS) $(HDRS) mdata_%.o $(CC) $(CFLAGS) $(LDLIBS) -o $@ $(@:mdata-%=mdata_%).o $(OBJS) $(CTFMERGE) -l mdata-client -o $@ $(OBJS) $(@:mdata-%=mdata_%).o # # Install Targets # .PHONY: install install: $(INSTALL_TARGETS) $(DESTDIR)$(BINDIR)/%: % @mkdir -p $(DESTDIR)$(BINDIR) cp $< $@ touch $@ $(DESTDIR)$(MANDIR)/%.$(MANSECT): man/man1m/%.1m @mkdir -p $(DESTDIR)$(MANDIR) sed 's/__SECT__/$(MANSECT)/g' < $< > $@ $(DESTDIR)/lib/smartdc/mdata-%: @mkdir -p $$(dirname $@) @rm -f $@ ln -s $(BINDIR)/$$(basename $@) $@ # # SmartOS (smartos-live) Package Manifest Targets # .PHONY: manifest manifest: cp manifest $(DESTDIR)/$(DESTNAME) .PHONY: update update: git pull --rebase # # Debian Package Targets # # .PHONY: package-debian package-debian: debuild -us -uc source-tarball: if [ -z $(RELEASE_DIRECTORY) ]; then \ echo "error: define RELEASE_DIRECTORY" >&2; \ exit 1; \ fi if [ -z $(PKGNAME) ]; then \ echo "error: define PKGNAME" >&2; \ exit 1; \ fi $(GNUTAR) \ -zc -f $(RELEASE_DIRECTORY)/$(PKGNAME)_$(VERSION).orig.tar.gz \ --exclude=.git \ --transform 's,^,$(PKGNAME)_$(VERSION)/,' \ * # # Cleanup Targets # .PHONY: clean clean: rm -f $(PROGS) $(OBJS) .PHONY: clobber clobber: clean rm -rf $(PWD)/proto mdata-client-0.0.1/README.md000066400000000000000000000003151223656244500153410ustar00rootroot00000000000000# mdata-client This repository contains metadata access tools for use within SmartOS (and SDC) guests. These guests may be either SmartOS Zones or KVM virtual machines. ## License MIT (See _LICENSE_.) mdata-client-0.0.1/base64.c000066400000000000000000000040661223656244500153210ustar00rootroot00000000000000/* * Copyright (c) 2013, Joyent, Inc. * See LICENSE file for copyright and license details. * * Portions based on Public Domain work obtained from: * https://shell.franken.de/svn/sky/xmlstorage/trunk/c++/xmlrpc/base64.cpp */ #include "stdlib.h" #include "stdio.h" #include "dynstr.h" #include "stdint.h" static const char base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz0123456789+/"; void base64_encode(uint8_t *input, size_t len, string_t *output) { char tmp[5]; unsigned int i = 0; tmp[4] = '\0'; dynstr_append(output, ""); while (i < len) { uint32_t c = input[i++] << 16; if (i < len) c |= input[i] << 8; i++; if (i < len) c |= input[i]; i++; tmp[0] = base64[(c & 0x00fc0000) >> 18]; tmp[1] = base64[(c & 0x0003f000) >> 12]; tmp[2] = i > len + 1 ? '=' : base64[(c & 0x00000fc0) >> 6]; tmp[3] = i > len ? '=' : base64[c & 0x0000003f]; dynstr_append(output, tmp); } } static int decode_one(char c) { if (c >= 'A' && c <= 'Z') return (c - 'A'); if (c >= 'a' && c <= 'z') return (c - 'a' + 26); if (c >= '0' && c <= '9') return (c - '0' + 52); if (c == '+') return (62); if (c == '/') return (63); if (c == '=') return (-1); return (-2); } int base64_decode(const char *input, size_t len, string_t *output) { int typ[4]; uint8_t buf[4]; unsigned int i, j; buf[3] = '\0'; dynstr_append(output, ""); /* * Valid encoded strings are a multiple of 4 characters long: */ if (len % 4 != 0) return (-1); for (i = 0; i < len; i += 4) { for (j = 0; j < 4; j++) typ[j] = decode_one(input[i + j]); /* * Filler must be contiguous on the right of the input * string, and at most two bytes: */ if (typ[0] == -1 || typ[1] == -1) return (-1); if (typ[2] == -1 && typ[3] != -1) return (-1); buf[0] = (typ[0] << 2) | (typ[1] >> 4); if (typ[2] != -1) buf[1] = ((typ[1] & 0x0f) << 4) | (typ[2] >>2); else buf[1] = '\0'; if (typ[3] != -1) buf[2] = ((typ[2] & 0x03) << 6) | typ[3]; else buf[2] = '\0'; dynstr_append(output, (const char *) buf); } return (0); } mdata-client-0.0.1/base64.h000066400000000000000000000005451223656244500153240ustar00rootroot00000000000000/* * Copyright (c) 2013, Joyent, Inc. * See LICENSE file for copyright and license details. */ #ifndef _BASE64_H #define _BASE64_H #include "dynstr.h" #ifdef __cplusplus extern "C" { #endif void base64_encode(const char *, size_t, string_t *); int base64_decode(const char *, size_t, string_t *); #ifdef __cplusplus } #endif #endif /* _BASE64_H */ mdata-client-0.0.1/common.c000066400000000000000000000005041223656244500155160ustar00rootroot00000000000000/* * Copyright (c) 2013, Joyent, Inc. * See LICENSE file for copyright and license details. */ #include #include int print_and_abort(const char *message, const char *file, int line) { (void) fprintf(stderr, "ASSERT: %s, file: %s @ line %d\n", message, file, line); abort(); return (0); } mdata-client-0.0.1/common.h000066400000000000000000000012211223656244500155200ustar00rootroot00000000000000/* * Copyright (c) 2013, Joyent, Inc. * See LICENSE file for copyright and license details. */ #ifndef _COMMON_H #define _COMMON_H #ifdef __cplusplus extern "C" { #endif #ifndef __HAVE_BOOLEAN_T typedef enum { B_FALSE = 0, B_TRUE = 1 } boolean_t; #endif /* !__HAVE_BOOLEAN_T */ #define __UNUSED __attribute__((unused)) #define VERIFY(EX) ((void)((EX) || print_and_abort("EX", __FILE__,__LINE__))) #define VERIFY0(EX) ((void)((EX) && print_and_abort("EX", __FILE__, __LINE__))) #define ABORT(MSG) print_and_abort((MSG), __FILE__,__LINE__) int print_and_abort(const char *, const char *, int); #ifdef __cplusplus } #endif #endif /* _COMMON_H */ mdata-client-0.0.1/crc32.c000066400000000000000000000072621223656244500151520ustar00rootroot00000000000000/* * Copyright (c) 2013, Joyent, Inc. * See LICENSE file for copyright and license details. */ #include #include #include #include "dynstr.h" /* * Pre-generated CRC32 Table from Polynomial 0xEDB88320: */ static uint32_t crc32_table[] = { 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d }; uint32_t crc32_calc(const char *cstr, size_t len) { uint32_t crc = 0xffffffff; unsigned int i; for (i = 0; i < len; i++) { uint8_t b = (uint8_t) cstr[i]; crc = crc32_table[(crc ^ b) & 0xff] ^ ((crc) >> 8); } return (~crc); } mdata-client-0.0.1/crc32.h000066400000000000000000000004371223656244500151540ustar00rootroot00000000000000/* * Copyright (c) 2013, Joyent, Inc. * See LICENSE file for copyright and license details. */ #ifndef _CRC32_H #define _CRC32_H #ifdef __cplusplus extern "C" { #endif #include uint32_t crc32_calc(const char *, int); #ifdef __cplusplus } #endif #endif /* _CRC32_H */ mdata-client-0.0.1/debian/000077500000000000000000000000001223656244500153055ustar00rootroot00000000000000mdata-client-0.0.1/debian/changelog000066400000000000000000000002411223656244500171540ustar00rootroot00000000000000joyent-mdata-client (0.0.1-1); urgency=low * Initial packaging for Joyent repository. -- Joshua M. Clulow Wed, 06 Nov 2013 16:54:10 -0800 mdata-client-0.0.1/debian/compat000066400000000000000000000000021223656244500165030ustar00rootroot000000000000008 mdata-client-0.0.1/debian/control000066400000000000000000000006271223656244500167150ustar00rootroot00000000000000Source: joyent-mdata-client Maintainer: Joshua M. Clulow Section: misc Priority: optional Standards-Version: 3.9.4 Build-Depends: debhelper (>= 8) Package: joyent-mdata-client Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends} Homepage: https://github.com/joyent/mdata-client Description: Metadata client tools for use in guest virtual machines hosted on a SmartOS hypervisor. mdata-client-0.0.1/debian/copyright000066400000000000000000000020701223656244500172370ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2013, Joyent, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. mdata-client-0.0.1/debian/rules000077500000000000000000000000361223656244500163640ustar00rootroot00000000000000#!/usr/bin/make -f %: dh $@ mdata-client-0.0.1/debian/source/000077500000000000000000000000001223656244500166055ustar00rootroot00000000000000mdata-client-0.0.1/debian/source/format000066400000000000000000000000141223656244500200130ustar00rootroot000000000000003.0 (quilt) mdata-client-0.0.1/dynstr.c000066400000000000000000000031531223656244500155540ustar00rootroot00000000000000/* * Copyright (c) 2013, Joyent, Inc. * See LICENSE file for copyright and license details. */ #include #include #include #include "dynstr.h" typedef struct string { size_t str_strlen; size_t str_datalen; char *str_data; } string_t; #define STRING_CHUNK_SIZE 64 void dynstr_reset(string_t *str) { if (str->str_data == NULL) return; str->str_strlen = 0; str->str_data[0] = '\0'; } size_t dynstr_len(string_t *str) { return (str->str_strlen); } const char * dynstr_cstr(string_t *str) { return (str->str_data); } void dynstr_appendc(string_t *str, char newc) { int chunksz = STRING_CHUNK_SIZE; if (str->str_strlen + 1 >= str->str_datalen) { str->str_datalen += chunksz; str->str_data = realloc(str->str_data, str->str_datalen); if (str->str_data == NULL) err(1, "could not allocate memory for string"); } str->str_data[str->str_strlen++] = newc; str->str_data[str->str_strlen] = '\0'; } void dynstr_append(string_t *str, const char *news) { int len = strlen(news); int chunksz = STRING_CHUNK_SIZE; while (chunksz < len) chunksz *= 2; if (len + str->str_strlen >= str->str_datalen) { str->str_datalen += chunksz; str->str_data = realloc(str->str_data, str->str_datalen); if (str->str_data == NULL) err(1, "could not allocate memory for string"); } strcpy(str->str_data + str->str_strlen, news); str->str_strlen += len; } string_t * dynstr_new(void) { string_t *ret = calloc(1, sizeof (string_t)); if (ret == NULL) err(10, "could not allocate memory for string"); return (ret); } void dynstr_free(string_t *str) { free(str->str_data); free(str); } mdata-client-0.0.1/dynstr.h000066400000000000000000000010041223656244500155520ustar00rootroot00000000000000/* * Copyright (c) 2013, Joyent, Inc. * See LICENSE file for copyright and license details. */ #ifndef _DYNSTR_H #define _DYNSTR_H #ifdef __cplusplus extern "C" { #endif typedef struct string string_t; string_t *dynstr_new(void); void dynstr_free(string_t *str); void dynstr_append(string_t *, const char *); void dynstr_appendc(string_t *, char); void dynstr_reset(string_t *str); size_t dynstr_len(string_t *str); const char *dynstr_cstr(string_t *str); #ifdef __cplusplus } #endif #endif /* _DYNSTR_H */ mdata-client-0.0.1/man/000077500000000000000000000000001223656244500146365ustar00rootroot00000000000000mdata-client-0.0.1/man/man1m/000077500000000000000000000000001223656244500156475ustar00rootroot00000000000000mdata-client-0.0.1/man/man1m/mdata-delete.1m000066400000000000000000000037271223656244500204450ustar00rootroot00000000000000.\" Copyright (c) 2013, Joyent, Inc. .\" See LICENSE file for copyright and license details. .TH "MDATA-DELETE" "__SECT__" "October 2013" "Joyent SmartDataCenter" "Metadata Commands" .SH "NAME" \fBmdata-delete\fR \-\- Delete a metadata key-value pair\. .SH "SYNOPSIS" . .nf \fB/usr/sbin/mdata-delete\fR \fIkeyname\fR .fi .SH "DESCRIPTION" .sp .LP The \fBmdata-delete\fR command allows the user (or a script) to modify the metadata for a guest instance running in a \fISmartDataCenter (SDC)\fR cloud. Metadata values are generally set programmatically via \fICloudAPI\fR, or during interactive provisioning via a Web Portal. They may also be set from within the instance with the \fBmdata-put\fR command. .sp .LP The key-value pair for \fIkeyname\fR will be removed permanently from the metadata stored for this guest instance. Deletion of a key which did not exist is not considered an error. .sp .LP If the metadata service is unavailable at the time of the request, this command will block waiting for it to become available. Non-transient failures will cause the program to exit with a non-zero status. Depending on the nature of the error, some diagnostic output may be printed to \fBstderr\fR. .SH "EXIT STATUS" .sp .LP The following exit values are returned: .sp .ne 2 .na \fB0\fR .ad .RS 5n Successful completion. .sp The key-value pair named \fIkeyname\fR was removed from the instance metadata, or did not initially exist. .RE .sp .ne 2 .na \fB2\fR .ad .RS 5n An error occurred. .sp An unexpected error condition occurred, which is believed to be a non-transient condition. Retrying the request is not expected to resolve the error condition; either a software bug or misconfiguration exists. .RE .sp .ne 2 .na \fB3\fR .ad .RS 5n A usage error occurred. .sp Malformed arguments were passed to the program. Check the usage instructions to ensure valid arguments are supplied. .RE .SH "SEE ALSO" .sp .LP \fBmdata-get\fR(__SECT__), \fBmdata-list\fR(__SECT__), \fBmdata-put\fR(__SECT__) mdata-client-0.0.1/man/man1m/mdata-get.1m000066400000000000000000000040141223656244500177500ustar00rootroot00000000000000.\" Copyright (c) 2013, Joyent, Inc. .\" See LICENSE file for copyright and license details. .TH "MDATA-GET" "__SECT__" "October 2013" "Joyent SmartDataCenter" "Metadata Commands" .SH "NAME" \fBmdata-get\fR \-\- Fetch the value of a metadata key-value pair\. .SH "SYNOPSIS" . .nf \fB/usr/sbin/mdata-get\fR \fIkeyname\fR .fi .SH "DESCRIPTION" .sp .LP The \fBmdata-get\fR command allows the user (or a script) to query the metadata for a guest instance running in a \fISmartDataCenter (SDC)\fR cloud. Metadata values are generally set programmatically via \fICloudAPI\fR, or during interactive provisioning via a Web Portal. They may also be set from within the instance with the \fBmdata-put\fR command. .sp .LP The value of the requested \fIkeyname\fR will be printed to \fBstdout\fR. If the metadata service is unavailable at the time of the request, this command will block waiting for it to become available. Non-transient failures, such as the non-existence of the requested \fIkeyname\fR, will cause the program to exit with a non-zero status. Depending on the nature of the error, some diagnostic output may be printed to \fBstderr\fR. .SH "EXIT STATUS" .sp .LP The following exit values are returned: .sp .ne 2 .na \fB0\fR .ad .RS 5n Successful completion. .sp The requested \fIkeyname\fR was available, and its value was emitted to \fBstdout\fR. .RE .sp .ne 2 .na \fB1\fR .ad .RS 5n Metadata value not found. .sp The requested \fIkeyname\fR was not found in the metadata. .RE .sp .ne 2 .na \fB2\fR .ad .RS 5n An error occurred. .sp An unexpected error condition occurred, which is believed to be a non-transient condition. Retrying the request is not expected to resolve the error condition; either a software bug or misconfiguration exists. .RE .sp .ne 2 .na \fB3\fR .ad .RS 5n A usage error occurred. .sp Malformed arguments were passed to the program. Check the usage instructions to ensure valid arguments are supplied. .RE .SH "SEE ALSO" .sp .LP \fBmdata-delete\fR(__SECT__), \fBmdata-list\fR(__SECT__), \fBmdata-put\fR(__SECT__) mdata-client-0.0.1/man/man1m/mdata-list.1m000066400000000000000000000036451223656244500201550ustar00rootroot00000000000000.\" Copyright (c) 2013, Joyent, Inc. .\" See LICENSE file for copyright and license details. .TH "MDATA-LIST" "__SECT__" "October 2013" "Joyent SmartDataCenter" "Metadata Commands" .SH "NAME" \fBmdata-list\fR \-\- Fetch the list of available metadata key-value pairs\. .SH "SYNOPSIS" . .nf \fB/usr/sbin/mdata-list\fR .fi .SH "DESCRIPTION" .sp .LP The \fBmdata-list\fR command allows the user (or a script) to query the metadata for a guest instance running in a \fISmartDataCenter (SDC)\fR cloud. Metadata values are generally set programmatically via \fICloudAPI\fR, or during interactive provisioning via a Web Portal. They may also be set from within the instance with the \fBmdata-put\fR command. The value of a key-value pair may be obtained by passing its name to the \fBmdata-get\fR command. .sp .LP The list of all customer-provided metadata key-value pairs will be printed to \fBstdout\fR. If the metadata service is unavailable at the time of the request, this command will block waiting for it to become available. Non-transient failures will cause the program to exit with a non-zero status. Depending on the nature of the error, some diagnostic output may be printed to \fBstderr\fR. .SH "EXIT STATUS" .sp .LP The following exit values are returned: .sp .ne 2 .na \fB0\fR .ad .RS 5n Successful completion. .sp The list of key-value pairs was emitted to \fBstdout\fR. .RE .sp .ne 2 .na \fB2\fR .ad .RS 5n An error occurred. .sp An unexpected error condition occurred, which is believed to be a non-transient condition. Retrying the request is not expected to resolve the error condition; either a software bug or misconfiguration exists. .RE .sp .ne 2 .na \fB3\fR .ad .RS 5n A usage error occurred. .sp Malformed arguments were passed to the program. Check the usage instructions to ensure valid arguments are supplied. .RE .SH "SEE ALSO" .sp .LP \fBmdata-delete\fR(__SECT__), \fBmdata-get\fR(__SECT__), \fBmdata-put\fR(__SECT__) mdata-client-0.0.1/man/man1m/mdata-put.1m000066400000000000000000000041431223656244500200040ustar00rootroot00000000000000.\" Copyright (c) 2013, Joyent, Inc. .\" See LICENSE file for copyright and license details. .TH "MDATA-PUT" "__SECT__" "October 2013" "Joyent SmartDataCenter" "Metadata Commands" .SH "NAME" \fBmdata-put\fR \-\- Set the value of a metadata key-value pair\. .SH "SYNOPSIS" . .nf \fB/usr/sbin/mdata-put\fR \fIkeyname\fR [ \fIvalue\fR ] .fi .SH "DESCRIPTION" .sp .LP The \fBmdata-put\fR command allows the user (or a script) to modify the metadata for a guest instance running in a \fISmartDataCenter (SDC)\fR cloud. Metadata values are generally set programmatically via \fICloudAPI\fR, or during interactive provisioning via a Web Portal. The value of a key-value pair may be obtained by passing its name to the \fBmdata-get\fR command. .sp .LP The key-value pair named \fIkeyname\fR will be updated in the metadata store for this instance. If a \fIvalue\fR argument is provided on the command-line, then that value will be used. Otherwise, if \fIstdin\fR is not a tty, the value will be read from \fIstdin\fR. .sp .LP If the metadata service is unavailable at the time of the request, this command will block waiting for it to become available. Non-transient failures, such as the non-existence of the requested \fIkeyname\fR, will cause the program to exit with a non-zero status. Depending on the nature of the error, some diagnostic output may be printed to \fBstderr\fR. .SH "EXIT STATUS" .sp .LP The following exit values are returned: .sp .ne 2 .na \fB0\fR .ad .RS 5n Successful completion. .sp The requested \fIkeyname\fR was valid, and its value was updated. .RE .sp .ne 2 .na \fB2\fR .ad .RS 5n An error occurred. .sp An unexpected error condition occurred, which is believed to be a non-transient condition. Retrying the request is not expected to resolve the error condition; either a software bug or misconfiguration exists. .RE .sp .ne 2 .na \fB3\fR .ad .RS 5n A usage error occurred. .sp Malformed arguments were passed to the program. Check the usage instructions to ensure valid arguments are supplied. .RE .SH "SEE ALSO" .sp .LP \fBmdata-delete\fR(__SECT__), \fBmdata-get\fR(__SECT__), \fBmdata-list\fR(__SECT__) mdata-client-0.0.1/manifest000066400000000000000000000005301223656244500156120ustar00rootroot00000000000000f usr/sbin/mdata-delete 0555 root bin f usr/sbin/mdata-get 0555 root bin f usr/sbin/mdata-list 0555 root bin f usr/sbin/mdata-put 0555 root bin f usr/share/man/man1m/mdata-delete.1m 0444 root bin f usr/share/man/man1m/mdata-get.1m 0444 root bin f usr/share/man/man1m/mdata-list.1m 0444 root bin f usr/share/man/man1m/mdata-put.1m 0444 root bin mdata-client-0.0.1/mdata_delete.c000066400000000000000000000033731223656244500166450ustar00rootroot00000000000000/* * Copyright (c) 2013, Joyent, Inc. * See LICENSE file for copyright and license details. */ #include #include #include #include #include #include #include #include #include #include #include "common.h" #include "dynstr.h" #include "plat.h" #include "proto.h" typedef enum mdata_exit_codes { MDEC_SUCCESS = 0, MDEC_NOTFOUND = 1, MDEC_ERROR = 2, MDEC_USAGE_ERROR = 3, MDEC_TRY_AGAIN = 10 } mdata_exit_codes_t; static char *keyname; static int print_response(mdata_response_t mdr, string_t *data) { switch (mdr) { case MDR_SUCCESS: return (MDEC_SUCCESS); case MDR_NOTFOUND: fprintf(stderr, "No metadata for '%s'\n", keyname); return (MDEC_NOTFOUND); case MDR_UNKNOWN: fprintf(stderr, "Error deleting metadata key '%s': %s\n", keyname, dynstr_cstr(data)); return (MDEC_ERROR); case MDR_INVALID_COMMAND: fprintf(stderr, "ERROR: host does not support DELETE\n"); return (MDEC_ERROR); default: ABORT("print_response: UNKNOWN RESPONSE\n"); return (MDEC_ERROR); } } int main(int argc, char **argv) { mdata_proto_t *mdp; mdata_response_t mdr; string_t *data; char *errmsg = NULL; if (argc < 2) { errx(MDEC_USAGE_ERROR, "Usage: %s ", argv[0]); } if (proto_init(&mdp, &errmsg) != 0) { fprintf(stderr, "ERROR: could not initialise protocol: %s\n", errmsg); return (MDEC_ERROR); } if (proto_version(mdp) < 2) { fprintf(stderr, "ERROR: host does not support DELETE\n"); return (MDEC_ERROR); } keyname = strdup(argv[1]); if (proto_execute(mdp, "DELETE", keyname, &mdr, &data) != 0) { fprintf(stderr, "ERROR: could not execute GET\n"); return (MDEC_ERROR); } return (print_response(mdr, data)); } mdata-client-0.0.1/mdata_get.c000066400000000000000000000034361223656244500161620ustar00rootroot00000000000000/* * Copyright (c) 2013, Joyent, Inc. * See LICENSE file for copyright and license details. */ #include #include #include #include #include #include #include #include #include #include #include "common.h" #include "dynstr.h" #include "plat.h" #include "proto.h" typedef enum mdata_exit_codes { MDEC_SUCCESS = 0, MDEC_NOTFOUND = 1, MDEC_ERROR = 2, MDEC_USAGE_ERROR = 3, MDEC_TRY_AGAIN = 10 } mdata_exit_codes_t; static char *keyname; static int print_response(mdata_response_t mdr, string_t *data) { const char *cstr = dynstr_cstr(data); size_t len = dynstr_len(data); switch (mdr) { case MDR_SUCCESS: fprintf(stdout, "%s", cstr); if (len < 1 || cstr[len - 1] != '\n') fprintf(stdout, "\n"); return (MDEC_SUCCESS); case MDR_NOTFOUND: fprintf(stderr, "No metadata for '%s'\n", keyname); return (MDEC_NOTFOUND); case MDR_UNKNOWN: fprintf(stderr, "Error getting metadata for key '%s': %s\n", keyname, cstr); return (MDEC_ERROR); case MDR_INVALID_COMMAND: fprintf(stderr, "ERROR: host does not support GET\n"); return (MDEC_ERROR); default: ABORT("print_response: UNKNOWN RESPONSE\n"); return (MDEC_ERROR); } } int main(int argc, char **argv) { mdata_proto_t *mdp; mdata_response_t mdr; string_t *data; char *errmsg = NULL; if (argc < 2) { errx(MDEC_USAGE_ERROR, "Usage: %s ", argv[0]); } if (proto_init(&mdp, &errmsg) != 0) { fprintf(stderr, "ERROR: could not initialise protocol: %s\n", errmsg); return (MDEC_ERROR); } keyname = strdup(argv[1]); if (proto_execute(mdp, "GET", keyname, &mdr, &data) != 0) { fprintf(stderr, "ERROR: could not execute GET\n"); return (MDEC_ERROR); } return (print_response(mdr, data)); } mdata-client-0.0.1/mdata_list.c000066400000000000000000000032001223656244500163430ustar00rootroot00000000000000/* * Copyright (c) 2013, Joyent, Inc. * See LICENSE file for copyright and license details. */ #include #include #include #include #include #include #include #include #include #include #include "common.h" #include "dynstr.h" #include "plat.h" #include "proto.h" typedef enum mdata_exit_codes { MDEC_SUCCESS = 0, MDEC_NOTFOUND = 1, MDEC_ERROR = 2, MDEC_USAGE_ERROR = 3, MDEC_TRY_AGAIN = 10 } mdata_exit_codes_t; static int print_response(mdata_response_t mdr, string_t *data) { const char *cstr = dynstr_cstr(data); size_t len = dynstr_len(data); switch (mdr) { case MDR_SUCCESS: fprintf(stdout, "%s", cstr); if (len >= 1 && cstr[len - 1] != '\n') fprintf(stdout, "\n"); return (MDEC_SUCCESS); case MDR_NOTFOUND: fprintf(stderr, "No metadata\n"); return (MDEC_NOTFOUND); case MDR_UNKNOWN: fprintf(stderr, "Error getting metadata: %s\n", cstr); return (MDEC_ERROR); case MDR_INVALID_COMMAND: fprintf(stderr, "ERROR: host does not support KEYS\n"); return (MDEC_ERROR); default: ABORT("print_response: UNKNOWN RESPONSE\n"); return (MDEC_ERROR); } } int main(int argc __UNUSED, char **argv __UNUSED) { mdata_proto_t *mdp; mdata_response_t mdr; string_t *data; char *errmsg = NULL; if (proto_init(&mdp, &errmsg) != 0) { fprintf(stderr, "ERROR: could not initialise protocol: %s\n", errmsg); return (MDEC_ERROR); } if (proto_execute(mdp, "KEYS", NULL, &mdr, &data) != 0) { fprintf(stderr, "ERROR: could not execute KEYS\n"); return (MDEC_ERROR); } return (print_response(mdr, data)); } mdata-client-0.0.1/mdata_put.c000066400000000000000000000052121223656244500162050ustar00rootroot00000000000000/* * Copyright (c) 2013, Joyent, Inc. * See LICENSE file for copyright and license details. */ #include #include #include #include #include #include #include #include #include #include #include "common.h" #include "dynstr.h" #include "plat.h" #include "proto.h" #include "base64.h" typedef enum mdata_exit_codes { MDEC_SUCCESS = 0, MDEC_NOTFOUND = 1, MDEC_ERROR = 2, MDEC_USAGE_ERROR = 3, MDEC_TRY_AGAIN = 10 } mdata_exit_codes_t; static char *keyname; static int print_response(mdata_response_t mdr, string_t *data) { switch (mdr) { case MDR_SUCCESS: return (MDEC_SUCCESS); case MDR_NOTFOUND: fprintf(stderr, "No metadata for '%s'\n", keyname); return (MDEC_NOTFOUND); case MDR_UNKNOWN: fprintf(stderr, "Error putting metadata for key '%s': %s\n", keyname, dynstr_cstr(data)); return (MDEC_ERROR); case MDR_INVALID_COMMAND: fprintf(stderr, "ERROR: host does not support PUT\n"); return (MDEC_ERROR); default: ABORT("print_response: UNKNOWN RESPONSE\n"); return (MDEC_ERROR); } } int main(int argc, char **argv) { mdata_proto_t *mdp; mdata_response_t mdr; string_t *data; char *errmsg = NULL; string_t *req = dynstr_new(); if (argc < 2) { errx(MDEC_USAGE_ERROR, "Usage: %s [ ]", argv[0]); } if (proto_init(&mdp, &errmsg) != 0) { fprintf(stderr, "ERROR: could not initialise protocol: %s\n", errmsg); return (MDEC_ERROR); } if (proto_version(mdp) < 2) { fprintf(stderr, "ERROR: host does not support PUT\n"); return (MDEC_ERROR); } keyname = strdup(argv[1]); base64_encode(argv[1], strlen(argv[1]), req); dynstr_appendc(req, ' '); if (argc >= 3) { /* * Use second argument as the value to put. */ base64_encode(argv[2], strlen(argv[2]), req); } else { int c; string_t *stdinstr = dynstr_new(); dynstr_append(stdinstr, ""); if (plat_is_interactive()) { fprintf(stderr, "ERROR: either specify the metadata " "value as the second command-line argument, or " "pipe content to stdin.\n"); return (MDEC_ERROR); } while ((c = fgetc(stdin)) != EOF) { dynstr_appendc(stdinstr, (char) c); } if (ferror(stdin)) { fprintf(stderr, "ERROR: could not read from stdin: " "%s\n", strerror(errno)); return (MDEC_ERROR); } base64_encode(dynstr_cstr(stdinstr), dynstr_len(stdinstr), req); dynstr_free(stdinstr); } if (proto_execute(mdp, "PUT", dynstr_cstr(req), &mdr, &data) != 0) { fprintf(stderr, "ERROR: could not execute GET\n"); return (MDEC_ERROR); } dynstr_free(req); return (print_response(mdr, data)); } mdata-client-0.0.1/plat.h000066400000000000000000000010371223656244500151750ustar00rootroot00000000000000/* * Copyright (c) 2013, Joyent, Inc. * See LICENSE file for copyright and license details. */ #ifndef _PLAT_H #define _PLAT_H #ifdef __cplusplus extern "C" { #endif #include "dynstr.h" typedef struct mdata_plat mdata_plat_t; int plat_is_interactive(void); /*int open_metadata_stream(FILE **fp, char **err);*/ int plat_init(mdata_plat_t **, char **er, int *); int plat_recv(mdata_plat_t *, string_t *, int); int plat_send(mdata_plat_t *, string_t *); void plat_fini(mdata_plat_t *); #ifdef __cplusplus } #endif #endif /* _PLAT_H */ mdata-client-0.0.1/plat/000077500000000000000000000000001223656244500150235ustar00rootroot00000000000000mdata-client-0.0.1/plat/linux.c000066400000000000000000000117021223656244500163270ustar00rootroot00000000000000/* * Copyright (c) 2013, Joyent, Inc. * See LICENSE file for copyright and license details. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "common.h" #include "plat.h" #include "dynstr.h" #include "plat/unix_common.h" #define SERIAL_DEVICE "/dev/ttyS1" typedef struct mdata_plat { int mpl_epoll; int mpl_conn; } mdata_plat_t; static int raw_mode(int fd, char **errmsg) { struct termios tios; if (tcgetattr(fd, &tios) == -1) { *errmsg = "could not set raw mode on serial device"; return (-1); } tios.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON); tios.c_oflag &= ~(OPOST); tios.c_cflag |= (CS8); tios.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG); /* * As described in "Case C: MIN = 0, TIME > 0" of termio(7I), this * configuration will block waiting for at least one character, or * the expiry of a 100 millisecond timeout: */ tios.c_cc[VMIN] = 0; tios.c_cc[VTIME] = 1; if (tcsetattr(fd, TCSAFLUSH, &tios) == -1) { *errmsg = "could not get attributes from serial device"; return (-1); } return (0); } static int open_md(int *outfd, char **errmsg, int *permfail) { int fd; char scrap[100]; ssize_t sz; struct flock l; if ((fd = open(SERIAL_DEVICE, O_RDWR | O_EXCL | O_NOCTTY)) == -1) { *errmsg = "Could not open serial device."; if (errno != EAGAIN && errno != EBUSY && errno != EINTR) *permfail = 1; return (-1); } /* * Lock the serial port for exclusive access: */ l.l_type = F_WRLCK; l.l_whence = SEEK_SET; l.l_start = l.l_len = 0; if (fcntl(fd, F_SETLKW, &l) == -1) { fprintf(stderr, "could not lock: %s\n", strerror(errno)); return (-1); } /* * Set raw mode on the serial port: */ if (raw_mode(fd, errmsg) == -1) { (void) close(fd); *permfail = 1; return (-1); } /* * Because this is a shared serial line, we may be part way through * a response from the remote peer. Read (and discard) data until we * cannot do so anymore: */ do { sz = read(fd, &scrap, sizeof (scrap)); if (sz == -1 && errno != EAGAIN) { *errmsg = "Failed to flush serial port before use."; (void) close(fd); return (-1); } } while (sz > 0); *outfd = fd; return (0); } int plat_send(mdata_plat_t *mpl, string_t *data) { int len = dynstr_len(data); if (write(mpl->mpl_conn, dynstr_cstr(data), len) != len) return (-1); return (0); } int plat_recv(mdata_plat_t *mpl, string_t *data, int timeout_ms) { for (;;) { struct epoll_event event; if (epoll_wait(mpl->mpl_epoll, &event, 1, timeout_ms) == -1) { fprintf(stderr, "epoll error: %d\n", errno); if (errno == EINTR) { return (-1); } err(1, "EPOLL_WAIT ERROR"); } if (event.data.fd == 0 || event.events == 0) { fprintf(stderr, "plat_recv timeout\n"); return (-1); } if (event.events & EPOLLIN) { char buf[2]; ssize_t sz; sz = read(mpl->mpl_conn, buf, 1); if (sz == 0) { fprintf(stderr, "WHAT, NO DATA?!\n"); continue; } if (buf[0] == '\n') { return (0); } else { buf[1] = '\0'; dynstr_append(data, buf); } } if (event.events & EPOLLERR) { fprintf(stderr, "POLLERR\n"); return (-1); } if (event.events & EPOLLHUP) { fprintf(stderr, "POLLHUP\n"); return (-1); } } return (-1); } void plat_fini(mdata_plat_t *mpl __UNUSED) { if (mpl != NULL) { if (mpl->mpl_epoll != -1) (void) close(mpl->mpl_epoll); if (mpl->mpl_conn != -1) (void) close(mpl->mpl_conn); free(mpl); } } static int plat_send_reset(mdata_plat_t *mpl) { int ret = -1; string_t *str = dynstr_new(); dynstr_append(str, "\n"); if (plat_send(mpl, str) != 0) goto bail; dynstr_reset(str); if (plat_recv(mpl, str, 2000) != 0) goto bail; if (strcmp(dynstr_cstr(str), "invalid command") != 0) goto bail; ret = 0; bail: dynstr_free(str); return (ret); } int plat_is_interactive(void) { return (unix_is_interactive()); } int plat_init(mdata_plat_t **mplout, char **errmsg, int *permfail) { mdata_plat_t *mpl = NULL; struct epoll_event event; if ((mpl = calloc(1, sizeof (*mpl))) == NULL) { *errmsg = "Could not allocate memory."; *permfail = 1; goto bail; } mpl->mpl_epoll = -1; mpl->mpl_conn = -1; if ((mpl->mpl_epoll = epoll_create(1)) == -1) { *errmsg = "Could not create epoll fd."; *permfail = 1; goto bail; } if (open_md(&mpl->mpl_conn, errmsg, permfail) != 0) { goto bail; } event.data.fd = mpl->mpl_conn; event.events = EPOLLIN | EPOLLERR | EPOLLHUP; if (epoll_ctl(mpl->mpl_epoll, EPOLL_CTL_ADD, mpl->mpl_conn, &event) == -1) { *errmsg = "Could not add conn to epoll context."; *permfail = 1; goto bail; } if (plat_send_reset(mpl) == -1) { *errmsg = "Could not do active reset."; goto bail; } *mplout = mpl; return (0); bail: plat_fini(mpl); return (-1); } mdata-client-0.0.1/plat/sunos.c000066400000000000000000000147131223656244500163440ustar00rootroot00000000000000/* * Copyright (c) 2013, Joyent, Inc. * See LICENSE file for copyright and license details. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "common.h" #include "dynstr.h" #include "plat.h" #include "plat/unix_common.h" #define IN_GLOBAL_DEVICE "/dev/term/b" static char *zone_md_socket_paths[] = { "/.zonecontrol/metadata.sock", /* SDC7 */ "/var/run/smartdc/metadata.sock", /* SDC6 */ NULL }; typedef struct mdata_plat { int mpl_port; int mpl_conn; } mdata_plat_t; static int find_product(smbios_hdl_t *shp, const smbios_struct_t *sp, void *arg) { char **outputp = arg; smbios_info_t info; if (sp->smbstr_type != SMB_TYPE_SYSTEM) return (0); if (smbios_info_common(shp, sp->smbstr_id, &info) != 0) return (0); if (info.smbi_product[0] != '\0') { *outputp = strdup(info.smbi_product); } return (0); } static char * get_product_string(void) { char *output = NULL; int e; smbios_hdl_t *shp; if ((shp = smbios_open(NULL, SMB_VERSION, 0, &e)) == NULL) { return (NULL); } smbios_iter(shp, find_product, &output); smbios_close(shp); return (output); } static int find_md_ngz(const char **out, int *permfail) { int i; struct stat st; /* * The location of the metadata socket has changed between SDC6 * and SDC7. Attempt to locate the one that exists in this instance: */ for (i = 0; zone_md_socket_paths[i] != NULL; i++) { if (lstat(zone_md_socket_paths[i], &st) == 0 && S_ISSOCK(st.st_mode)) { *out = zone_md_socket_paths[i]; return (0); } else { /* * If we're not root, and we get an EACCES, it's * often a permissions problem. Don't retry * forever: */ if (geteuid() != 0 && (errno == EPERM || errno == EACCES)) *permfail = 1; } } return (-1); } static int open_md_ngz(int *outfd, char **errmsg, int *permfail) { /* * We're in a non-global zone, so try and connect to the * metadata socket: */ long on = 1L; int fd; const char *sockpath; struct sockaddr_un ua; /* * This is not always a permanent failure, because the metadata * socket might not exist yet. Keep trying and wait for it to * appear. */ if (find_md_ngz(&sockpath, permfail) == -1) { *errmsg = "Could not find metadata socket."; return (-1); } if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { *errmsg = "Could not open metadata socket."; *permfail = 1; return (-1); } /* * Enable non-blocking I/O on the socket so that we can time-out * when we want to: */ if (ioctl(fd, (int)FIONBIO, &on) != 0) { *errmsg = "Could not set non-blocking I/O on socket."; (void) close(fd); *permfail = 1; return (-1); } bzero(&ua, sizeof (ua)); ua.sun_family = AF_UNIX; strcpy(ua.sun_path, sockpath); if (connect(fd, (struct sockaddr *)&ua, sizeof (ua)) == -1) { (void) close(fd); *errmsg = "Could not connect metadata socket."; return (-1); } *outfd = fd; return (0); } static int open_md_gz(int *outfd, char **errmsg, int *permfail) { /* * We're in a global zone in a SmartOS KVM/QEMU instance, so * try to use /dev/term/b for metadata. */ return (unix_open_serial(IN_GLOBAL_DEVICE, outfd, errmsg, permfail)); } int plat_send(mdata_plat_t *mpl, string_t *data) { int len = dynstr_len(data); if (write(mpl->mpl_conn, dynstr_cstr(data), len) != len) return (-1); return (0); } int plat_recv(mdata_plat_t *mpl, string_t *data, int timeout_ms) { port_event_t pev; timespec_t tv; for (;;) { if (port_associate(mpl->mpl_port, PORT_SOURCE_FD, mpl->mpl_conn, POLLIN | POLLERR | POLLHUP , NULL) != 0) { fprintf(stderr, "port_associate error: %s\n", strerror(errno)); return (-1); } tv.tv_sec = timeout_ms / 1000; timeout_ms -= tv.tv_sec * 1000; tv.tv_nsec = timeout_ms * 1000 * 1000; /* 100ms */ if (port_get(mpl->mpl_port, &pev, &tv) == -1) { if (errno == ETIME) { fprintf(stderr, "plat_recv timeout\n"); return (-1); } fprintf(stderr, "port_get error: %s\n", strerror(errno)); return (-1); } if (pev.portev_events & POLLIN) { char buf[2]; ssize_t sz; if ((sz = read(mpl->mpl_conn, buf, 1)) > 0) { if (buf[0] == '\n') { return (0); } else { buf[1] = '\0'; dynstr_append(data, buf); } } } if (pev.portev_events & POLLERR) { fprintf(stderr, "POLLERR\n"); return (-1); } if (pev.portev_events & POLLHUP) { fprintf(stderr, "POLLHUP\n"); return (-1); } } return (-1); } void plat_fini(mdata_plat_t *mpl) { if (mpl != NULL) { if (mpl->mpl_port != -1) (void) close(mpl->mpl_port); if (mpl->mpl_conn != -1) (void) close(mpl->mpl_conn); free(mpl); } } static int plat_send_reset(mdata_plat_t *mpl) { int ret = -1; string_t *str = dynstr_new(); dynstr_append(str, "\n"); if (plat_send(mpl, str) != 0) goto bail; dynstr_reset(str); if (plat_recv(mpl, str, 2000) != 0) goto bail; if (strcmp(dynstr_cstr(str), "invalid command") != 0) goto bail; ret = 0; bail: dynstr_free(str); return (ret); } int plat_is_interactive(void) { return (unix_is_interactive()); } int plat_init(mdata_plat_t **mplout, char **errmsg, int *permfail) { char *product; boolean_t smartdc_hvm_guest = B_FALSE; mdata_plat_t *mpl = NULL; if ((mpl = calloc(1, sizeof (*mpl))) == NULL) { *errmsg = "Could not allocate memory."; *permfail = 1; goto bail; } mpl->mpl_port = -1; mpl->mpl_conn = -1; if ((mpl->mpl_port = port_create()) == -1) { *errmsg = "Could not create event port."; *permfail = 1; goto bail; } if (getzoneid() != GLOBAL_ZONEID) { if (open_md_ngz(&mpl->mpl_conn, errmsg, permfail) != 0) goto bail; goto wrapfd; } /* * Interrogate the SMBIOS data from the system to see if we're * in a KVM/QEMU virtual machine: */ product = get_product_string(); if (product != NULL && strcmp(product, "SmartDC HVM") == 0) smartdc_hvm_guest = B_TRUE; free(product); if (smartdc_hvm_guest) { if (open_md_gz(&mpl->mpl_conn, errmsg, permfail) != 0) goto bail; goto wrapfd; } /* * We have no idea. */ *errmsg = "I don't know how to get metadata on this system."; *permfail = 1; goto bail; wrapfd: if (plat_send_reset(mpl) == -1) { *errmsg = "Could not do active reset."; goto bail; } *mplout = mpl; return (0); bail: plat_fini(mpl); return (-1); } mdata-client-0.0.1/plat/unix_common.c000066400000000000000000000044141223656244500175250ustar00rootroot00000000000000/* * Copyright (c) 2013, Joyent, Inc. * See LICENSE file for copyright and license details. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "common.h" #include "plat.h" #include "dynstr.h" int unix_is_interactive(void) { return (isatty(STDIN_FILENO) == 1); } static int unix_raw_mode(int fd, char **errmsg) { struct termios tios; if (tcgetattr(fd, &tios) == -1) { *errmsg = "could not set raw mode on serial device"; return (-1); } tios.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON); tios.c_oflag &= ~(OPOST); tios.c_cflag |= (CS8); tios.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG); /* * As described in "Case C: MIN = 0, TIME > 0" of termio(7I), this * configuration will block waiting for at least one character, or * the expiry of a 100 millisecond timeout: */ tios.c_cc[VMIN] = 0; tios.c_cc[VTIME] = 1; if (tcsetattr(fd, TCSAFLUSH, &tios) == -1) { *errmsg = "could not get attributes from serial device"; return (-1); } return (0); } int unix_open_serial(char *devpath, int *outfd, char **errmsg, int *permfail) { int fd; char scrap[100]; ssize_t sz; struct flock l; if ((fd = open(devpath, O_RDWR | O_EXCL | O_NOCTTY)) == -1) { *errmsg = "Could not open serial device."; if (errno != EAGAIN && errno != EBUSY && errno != EINTR) *permfail = 1; return (-1); } /* * Lock the serial port for exclusive access: */ l.l_type = F_WRLCK; l.l_whence = SEEK_SET; l.l_start = l.l_len = 0; if (fcntl(fd, F_SETLKW, &l) == -1) { *errmsg = "Could not lock serial device."; return (-1); } /* * Set raw mode on the serial port: */ if (unix_raw_mode(fd, errmsg) == -1) { (void) close(fd); *permfail = 1; return (-1); } /* * Because this is a shared serial line, we may be part way through * a response from the remote peer. Read (and discard) data until we * cannot do so anymore: */ do { sz = read(fd, &scrap, sizeof (scrap)); if (sz == -1 && errno != EAGAIN) { *errmsg = "Failed to flush serial port before use."; (void) close(fd); return (-1); } } while (sz > 0); *outfd = fd; return (0); } mdata-client-0.0.1/plat/unix_common.h000066400000000000000000000007401223656244500175300ustar00rootroot00000000000000/* * Copyright (c) 2013, Joyent, Inc. * See LICENSE file for copyright and license details. */ #ifndef _UNIX_COMMON_H #define _UNIX_COMMON_H #ifdef __cplusplus extern "C" { #endif #include "plat.h" #include "dynstr.h" /*int unix_raw_mode(int fd, char **errmsg);*/ int unix_open_serial(char *devpath, int *outfd, char **errmsg, int *permfail); int unix_send_reset(mdata_plat_t *mpl); int unix_is_interactive(void); #ifdef __cplusplus } #endif #endif /* _UNIX_COMMON_H */ mdata-client-0.0.1/proto.c000066400000000000000000000311601223656244500153730ustar00rootroot00000000000000/* * Copyright (c) 2013, Joyent, Inc. * See LICENSE file for copyright and license details. */ #include #include #include #include #include #include #include #include #include #include #include "common.h" #include "dynstr.h" #include "plat.h" #include "proto.h" #include "reqid.h" #include "crc32.h" #include "base64.h" #define RECV_TIMEOUT_MS 6000 typedef enum mdata_proto_state { MDPS_MESSAGE_HEADER = 1, MDPS_MESSAGE_DATA, MDPS_MESSAGE_V2, MDPS_READY, MDPS_ERROR } mdata_proto_state_t; typedef enum mdata_proto_version { MDPV_VERSION_1 = 1, MDPV_VERSION_2 = 2 } mdata_proto_version_t; typedef struct mdata_command { char mdc_reqid[REQID_LEN]; string_t *mdc_request; string_t *mdc_response_data; mdata_response_t mdc_response; int mdc_done; } mdata_command_t; typedef struct mdata_proto { mdata_plat_t *mdp_plat; mdata_command_t *mdp_command; mdata_proto_state_t mdp_state; mdata_proto_version_t mdp_version; boolean_t mdp_in_reset; char *mdp_errmsg; char *mdp_parse_errmsg; } mdata_proto_t; static int proto_send(mdata_proto_t *mdp); static int proto_recv(mdata_proto_t *mdp); static int proto_negotiate(mdata_proto_t *mdp) { mdata_command_t *mdcsave; mdata_response_t mdr; string_t *rdata = NULL; int ret = -1; mdcsave = mdp->mdp_command; mdp->mdp_command = NULL; /* * Assume Protocol Version 1 until we negotiate up to Version 2. */ mdp->mdp_version = MDPV_VERSION_1; if (proto_execute(mdp, "NEGOTIATE", "V2", &mdr, &rdata) == 0) { if (mdr == MDR_V2_OK) mdp->mdp_version = MDPV_VERSION_2; ret = 0; } mdp->mdp_command = mdcsave; if (rdata != NULL) dynstr_free(rdata); return (ret); } static int proto_reset(mdata_proto_t *mdp) { int permfail = 0; /* * Prevent proto_execute() from calling back into proto_reset() * while we're resetting: */ mdp->mdp_in_reset = B_TRUE; retry: mdp->mdp_errmsg = NULL; /* * Close our existing platform-specific code handle if we have * one open: */ if (mdp->mdp_plat != NULL) { plat_fini(mdp->mdp_plat); mdp->mdp_plat = NULL; } mdp->mdp_state = MDPS_READY; /* * Initialise the platform-specific code: */ if (plat_init(&mdp->mdp_plat, &mdp->mdp_errmsg, &permfail) == -1) { if (permfail) { return (-1); } else { sleep(1); goto retry; } } /* * Determine what protocol our host supports: */ if (proto_negotiate(mdp) == -1) { sleep(1); goto retry; } mdp->mdp_in_reset = B_FALSE; mdp->mdp_errmsg = NULL; return (0); } static int proto_parse_v2(mdata_proto_t *mdp, string_t *input, string_t *request_id, string_t *command, string_t *response_data) { const char *endp = dynstr_cstr(input); unsigned long clen; uint32_t crc32; mdp->mdp_parse_errmsg = NULL; if (strstr(endp, "V2 ") != endp) { mdp->mdp_parse_errmsg = "message did not start with V2"; return (-1); } endp += 3; /* * Read Content Length: */ if ((clen = strtoul(endp, (char **) &endp, 10)) == 0) { mdp->mdp_parse_errmsg = "invalid content length"; return (-1); } /* * Skip whitespace: */ while (*endp == ' ') endp++; /* * Read CRC32 checksum: */ if ((crc32 = strtoul(endp, (char **) &endp, 16)) == 0) { mdp->mdp_parse_errmsg = "invalid crc32 in frame"; return (-1); } /* * Skip whitespace: */ while (*endp == ' ') endp++; /* * Ensure Content Length and CRC32 values from header match * reality: */ if (strlen(endp) != clen || crc32_calc(endp, clen) != crc32) { mdp->mdp_parse_errmsg = "clen/crc32 mismatch"; return (-1); } /* * Read Request ID: */ while (*endp != ' ' && *endp != '\0') { dynstr_appendc(request_id, *endp++); } if (dynstr_len(request_id) == 0) { mdp->mdp_parse_errmsg = "missing request id"; return (-1); } /* * Skip whitespace: */ while (*endp == ' ') endp++; /* * Read Command/Code: */ while (*endp != ' ' && *endp != '\0') { dynstr_appendc(command, *endp++); } if (dynstr_len(command) == 0) { mdp->mdp_parse_errmsg = "missing command/code"; return (-1); } /* * Skip Whitespace: */ while (*endp == ' ') endp++; /* * Read the Response Data: */ if (base64_decode(endp, strlen(endp), response_data) == -1) { mdp->mdp_parse_errmsg = "base64 error"; return (-1); } return (0); } static void process_input(mdata_proto_t *mdp, string_t *input) { const char *cstr = dynstr_cstr(input); string_t *command, *request_id; switch (mdp->mdp_state) { case MDPS_MESSAGE_V2: command = dynstr_new(); request_id = dynstr_new(); dynstr_reset(mdp->mdp_command->mdc_response_data); if (proto_parse_v2(mdp, input, request_id, command, mdp->mdp_command->mdc_response_data) == -1) { /* * XXX Presently, drop frames that we can't * parse. */ } else if (strcmp(dynstr_cstr(request_id), mdp->mdp_command->mdc_reqid) != 0) { /* * XXX Presently, drop frames that are not for * the currently outstanding request. */ } else if (strcmp(dynstr_cstr(command), "NOTFOUND") == 0) { mdp->mdp_state = MDPS_READY; mdp->mdp_command->mdc_response = MDR_NOTFOUND; mdp->mdp_command->mdc_done = 1; } else if (strcmp(dynstr_cstr(command), "SUCCESS") == 0) { mdp->mdp_state = MDPS_READY; mdp->mdp_command->mdc_response = MDR_SUCCESS; mdp->mdp_command->mdc_done = 1; } else { mdp->mdp_state = MDPS_READY; mdp->mdp_command->mdc_response = MDR_UNKNOWN; mdp->mdp_command->mdc_done = 1; } dynstr_free(command); dynstr_free(request_id); break; case MDPS_MESSAGE_HEADER: if (strcmp(cstr, "NOTFOUND") == 0) { mdp->mdp_state = MDPS_READY; mdp->mdp_command->mdc_response = MDR_NOTFOUND; mdp->mdp_command->mdc_done = 1; } else if (strcmp(cstr, "SUCCESS") == 0) { mdp->mdp_state = MDPS_MESSAGE_DATA; mdp->mdp_command->mdc_response = MDR_SUCCESS; } else if (strcmp(cstr, "V2_OK") == 0) { mdp->mdp_state = MDPS_READY; mdp->mdp_command->mdc_response = MDR_V2_OK; mdp->mdp_command->mdc_done = 1; } else if (strcmp(cstr, "invalid command") == 0) { mdp->mdp_state = MDPS_READY; mdp->mdp_command->mdc_response = MDR_INVALID_COMMAND; mdp->mdp_command->mdc_done = 1; } else { mdp->mdp_state = MDPS_READY; dynstr_append(mdp->mdp_command->mdc_response_data, cstr); mdp->mdp_command->mdc_response = MDR_UNKNOWN; mdp->mdp_command->mdc_done = 1; } break; case MDPS_MESSAGE_DATA: if (strcmp(cstr, ".") == 0) { mdp->mdp_state = MDPS_READY; mdp->mdp_command->mdc_done = 1; } else { string_t *respdata = mdp->mdp_command->mdc_response_data; int offs = cstr[0] == '.' ? 1 : 0; if (dynstr_len(respdata) > 0) dynstr_append(respdata, "\n"); dynstr_append(respdata, cstr + offs); } break; case MDPS_READY: case MDPS_ERROR: break; default: ABORT("process_input: UNKNOWN STATE\n"); } } static int proto_send(mdata_proto_t *mdp) { VERIFY(mdp->mdp_command); if (plat_send(mdp->mdp_plat, mdp->mdp_command->mdc_request) == -1) { mdp->mdp_state = MDPS_ERROR; return (-1); } /* * Wait for response header from remote peer: */ switch (mdp->mdp_version) { case MDPV_VERSION_1: mdp->mdp_state = MDPS_MESSAGE_HEADER; break; case MDPV_VERSION_2: mdp->mdp_state = MDPS_MESSAGE_V2; break; default: ABORT("unknown protocol version"); } return (0); } static int proto_recv(mdata_proto_t *mdp) { int ret = -1; string_t *line = dynstr_new(); for (;;) { if (plat_recv(mdp->mdp_plat, line, RECV_TIMEOUT_MS) == -1) { mdp->mdp_state = MDPS_ERROR; goto bail; } process_input(mdp, line); dynstr_reset(line); if (mdp->mdp_command->mdc_done) break; } ret = 0; bail: dynstr_free(line); return (ret); } /* * Version 2 of the Metadata Protocol is, after a fashion, a framed * protocol. Each 'frame' is really just a LF-terminated line of * text. Request and Response payloads are BASE64-encoded to allow the * transmission of embedded LFs, and other potentially binary data. * * The line format is as follows: * ,- Body (CRC32/Length is of * _____________________ <--/ this string) * V2 21 265ae1d8 dc4fae17 SUCCESS W10=\n * ^ ^ ^ ^ ^ ^ ^ * | | | | | | \--- Terminating Linefeed. * | | | | | \------- BASE64-encoded payload. * | | | | \--------------- Request Command or Response Code * | | | \------------------------ Request ID (8 random hex digits) * | | \--------------------------------- CRC32 of Body as 8 hex digits * | \------------------------------------ Content Length of Body, base 10 * \-------------------------------------- So that this can be a V1 command * as well, we start with "V2". * This will be a FAILURE on a * host that only supports the * V1 protocol. */ static void proto_make_request_v2(const char *command, const char *argument, string_t *output, char *reqidbuf) { char strbuf[23 + 1 + 8 + 1]; /* strlen(UINT64_MAX) + ' ' + %08x + \0 */ string_t *body = dynstr_new(); /* * Generate the BODY of the V2 message, which is the portion we * use when generating the Content Length and CRC32 checksum for * the message HEADER. */ dynstr_append(body, reqid(reqidbuf)); dynstr_append(body, " "); dynstr_append(body, command); if (argument != NULL) { dynstr_append(body, " "); base64_encode(argument, strlen(argument), body); } /* * Generate the HEADER directly into the output, and then * append the BODY: */ dynstr_append(output, "V2 "); sprintf(strbuf, "%u %08x", (unsigned int) dynstr_len(body), crc32_calc( dynstr_cstr(body), dynstr_len(body))); dynstr_append(output, strbuf); dynstr_append(output, " "); dynstr_append(output, dynstr_cstr(body)); dynstr_append(output, "\n"); dynstr_free(body); } static void proto_make_request_v1(const char *command, const char *argument, string_t *output) { dynstr_append(output, command); if (argument != NULL) { dynstr_append(output, " "); dynstr_append(output, argument); } dynstr_append(output, "\n"); } int proto_execute(mdata_proto_t *mdp, const char *command, const char *argument, mdata_response_t *response, string_t **response_data) { mdata_command_t mdc; /* * Initialise new command structure: */ bzero(&mdc, sizeof (mdc)); mdc.mdc_request = dynstr_new(); mdc.mdc_response_data = dynstr_new(); mdc.mdc_response = MDR_PENDING; mdc.mdc_done = 0; VERIFY0(mdp->mdp_command); mdp->mdp_command = &mdc; retry: /* * (Re-)generate request string to send to remote peer: */ dynstr_reset(mdc.mdc_request); switch (mdp->mdp_version) { case MDPV_VERSION_1: proto_make_request_v1(command, argument, mdc.mdc_request); break; case MDPV_VERSION_2: proto_make_request_v2(command, argument, mdc.mdc_request, mdc.mdc_reqid); break; default: ABORT("unknown protocol version"); } /* * Attempt to send the request to the remote peer: */ if (mdp->mdp_state == MDPS_ERROR || proto_send(mdp) != 0 || proto_recv(mdp) != 0) { /* * Discard existing response data and reset the command * state: */ dynstr_reset(mdp->mdp_command->mdc_response_data); mdc.mdc_response = MDR_PENDING; /* * If the command we're trying to send is part of a * protocol reset sequence, just fail immediately: */ if (mdp->mdp_in_reset) goto bail; /* * We could not send the request, so reset the stream * and try again: */ fprintf(stderr, "receive timeout, resetting " "protocol...\n"); if (proto_reset(mdp) == -1) { /* * We could not do a reset, so abort the whole * thing. */ fprintf(stderr, "ERROR: while resetting connection: " "%s\n", mdp->mdp_errmsg); goto bail; } else { /* * We were able to reset OK, so keep trying. */ goto retry; } } if (mdp->mdp_state != MDPS_READY) ABORT("proto state not MDPS_READY\n"); /* * We were able to send a command and receive a response. * Examine the response and decide what to do: */ *response = mdc.mdc_response; *response_data = mdc.mdc_response_data; dynstr_free(mdc.mdc_request); mdp->mdp_command = NULL; return (0); bail: dynstr_free(mdc.mdc_request); dynstr_free(mdc.mdc_response_data); mdp->mdp_command = NULL; return (-1); } int proto_version(mdata_proto_t *mdp) { return (mdp->mdp_version); } int proto_init(mdata_proto_t **out, char **errmsg) { mdata_proto_t *mdp; reqid_init(); if ((mdp = calloc(1, sizeof (*mdp))) == NULL) return (-1); if (proto_reset(mdp) == -1) { *errmsg = mdp->mdp_errmsg; free(mdp); return (-1); } *out = mdp; return (0); } mdata-client-0.0.1/proto.h000066400000000000000000000011221223656244500153730ustar00rootroot00000000000000/* * Copyright (c) 2013, Joyent, Inc. * See LICENSE file for copyright and license details. */ #ifndef _PROTO_H #define _PROTO_H #ifdef __cplusplus extern "C" { #endif typedef enum mdata_response { MDR_UNKNOWN = 1, MDR_NOTFOUND, MDR_SUCCESS, MDR_INVALID_COMMAND, MDR_PENDING, MDR_V2_OK } mdata_response_t; typedef struct mdata_proto mdata_proto_t; int proto_init(mdata_proto_t **, char **); int proto_version(mdata_proto_t *); int proto_execute(mdata_proto_t *, const char *, const char *, mdata_response_t *, string_t **); #ifdef __cplusplus } #endif #endif /* _PROTO_H */ mdata-client-0.0.1/reqid.c000066400000000000000000000021241223656244500153320ustar00rootroot00000000000000/* * Copyright (c) 2013, Joyent, Inc. * See LICENSE file for copyright and license details. */ #include #include #include #include #include #include #include #include #include #include "common.h" #include "reqid.h" static int urandom_fd = -1; char * reqid(char *buf) { int i; static int seed = -1; uint32_t tmp = 0; VERIFY(buf != NULL); /* * If we were able to open it, try and read a random request ID * from /dev/urandom: */ if (urandom_fd != -1) { if (read(urandom_fd, &tmp, sizeof (tmp)) == sizeof (tmp)) goto out; } /* * Otherwise, fall back to C rand(): */ if (seed == -1) { seed = (int) time(NULL); srand(seed); } for (i = 0; i < 4; i++) { tmp |= (0xff & (rand())) << (i * 8); } out: sprintf(buf, "%08x", tmp); return (buf); } int reqid_init(void) { if (urandom_fd == -1) urandom_fd = open("/dev/urandom", O_RDONLY | O_NONBLOCK); return (0); } void reqid_fini(void) { if (urandom_fd == -1) return; urandom_fd = -1; (void) close(urandom_fd); } mdata-client-0.0.1/reqid.h000066400000000000000000000004751223656244500153460ustar00rootroot00000000000000/* * Copyright (c) 2013, Joyent, Inc. * See LICENSE file for copyright and license details. */ #ifndef _REQID_H #define _REQID_H #ifdef __cplusplus extern "C" { #endif #define REQID_LEN 9 int reqid_init(void); void reqid_fini(void); char * reqid(char *buf); #ifdef __cplusplus } #endif #endif /* _REQID_H */