Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  util.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0-only
/*
 * vsock test utilities
 *
 * Copyright (C) 2017 Red Hat, Inc.
 *
 * Author: Stefan Hajnoczi <stefanha@redhat.com>
 */


#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <assert.h>
#include <sys/epoll.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <linux/sockios.h>

#include "timeout.h"
#include "control.h"
#include "util.h"

#define KALLSYMS_PATH  "/proc/kallsyms"
#define KALLSYMS_LINE_LEN 512

/* Install signal handlers */
void init_signals(void)
{
 struct sigaction act = {
  .sa_handler = sigalrm,
 };

 sigaction(SIGALRM, &act, NULL);
 signal(SIGPIPE, SIG_IGN);
}

static unsigned int parse_uint(const char *str, const char *err_str)
{
 char *endptr = NULL;
 unsigned long n;

 errno = 0;
 n = strtoul(str, &endptr, 10);
 if (errno || *endptr != '\0') {
  fprintf(stderr, "malformed %s \"%s\"\n", err_str, str);
  exit(EXIT_FAILURE);
 }
 return n;
}

/* Parse a CID in string representation */
unsigned int parse_cid(const char *str)
{
 return parse_uint(str, "CID");
}

/* Parse a port in string representation */
unsigned int parse_port(const char *str)
{
 return parse_uint(str, "port");
}

/* Wait for the remote to close the connection */
void vsock_wait_remote_close(int fd)
{
 struct epoll_event ev;
 int epollfd, nfds;

 epollfd = epoll_create1(0);
 if (epollfd == -1) {
  perror("epoll_create1");
  exit(EXIT_FAILURE);
 }

 ev.events = EPOLLRDHUP | EPOLLHUP;
 ev.data.fd = fd;
 if (epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev) == -1) {
  perror("epoll_ctl");
  exit(EXIT_FAILURE);
 }

 nfds = epoll_wait(epollfd, &ev, 1, TIMEOUT * 1000);
 if (nfds == -1) {
  perror("epoll_wait");
  exit(EXIT_FAILURE);
 }

 if (nfds == 0) {
  fprintf(stderr, "epoll_wait timed out\n");
  exit(EXIT_FAILURE);
 }

 assert(nfds == 1);
 assert(ev.events & (EPOLLRDHUP | EPOLLHUP));
 assert(ev.data.fd == fd);

 close(epollfd);
}

/* Wait until ioctl gives an expected int value.
 * Return false if the op is not supported.
 */

bool vsock_ioctl_int(int fd, unsigned long op, int expected)
{
 int actual, ret;
 char name[32];

 snprintf(name, sizeof(name), "ioctl(%lu)", op);

 timeout_begin(TIMEOUT);
 do {
  ret = ioctl(fd, op, &actual);
  if (ret < 0) {
   if (errno == EOPNOTSUPP || errno == ENOTTY)
    break;

   perror(name);
   exit(EXIT_FAILURE);
  }
  timeout_check(name);
 } while (actual != expected);
 timeout_end();

 return ret >= 0;
}

/* Wait until transport reports no data left to be sent.
 * Return false if transport does not implement the unsent_bytes() callback.
 */

bool vsock_wait_sent(int fd)
{
 return vsock_ioctl_int(fd, SIOCOUTQ, 0);
}

/* Create socket <type>, bind to <cid, port>.
 * Return the file descriptor, or -1 on error.
 */

int vsock_bind_try(unsigned int cid, unsigned int port, int type)
{
 struct sockaddr_vm sa = {
  .svm_family = AF_VSOCK,
  .svm_cid = cid,
  .svm_port = port,
 };
 int fd, saved_errno;

 fd = socket(AF_VSOCK, type, 0);
 if (fd < 0) {
  perror("socket");
  exit(EXIT_FAILURE);
 }

 if (bind(fd, (struct sockaddr *)&sa, sizeof(sa))) {
  saved_errno = errno;
  close(fd);
  errno = saved_errno;
  fd = -1;
 }

 return fd;
}

/* Create socket <type>, bind to <cid, port> and return the file descriptor. */
int vsock_bind(unsigned int cid, unsigned int port, int type)
{
 int fd;

 fd = vsock_bind_try(cid, port, type);
 if (fd < 0) {
  perror("bind");
  exit(EXIT_FAILURE);
 }

 return fd;
}

int vsock_connect_fd(int fd, unsigned int cid, unsigned int port)
{
 struct sockaddr_vm sa = {
  .svm_family = AF_VSOCK,
  .svm_cid = cid,
  .svm_port = port,
 };
 int ret;

 timeout_begin(TIMEOUT);
 do {
  ret = connect(fd, (struct sockaddr *)&sa, sizeof(sa));
  timeout_check("connect");
 } while (ret < 0 && errno == EINTR);
 timeout_end();

 return ret;
}

/* Bind to <bind_port>, connect to <cid, port> and return the file descriptor. */
int vsock_bind_connect(unsigned int cid, unsigned int port, unsigned int bind_port, int type)
{
 int client_fd;

 client_fd = vsock_bind(VMADDR_CID_ANY, bind_port, type);

 if (vsock_connect_fd(client_fd, cid, port)) {
  perror("connect");
  exit(EXIT_FAILURE);
 }

 return client_fd;
}

/* Connect to <cid, port> and return the file descriptor. */
int vsock_connect(unsigned int cid, unsigned int port, int type)
{
 int fd;

 control_expectln("LISTENING");

 fd = socket(AF_VSOCK, type, 0);
 if (fd < 0) {
  perror("socket");
  exit(EXIT_FAILURE);
 }

 if (vsock_connect_fd(fd, cid, port)) {
  int old_errno = errno;

  close(fd);
  fd = -1;
  errno = old_errno;
 }

 return fd;
}

int vsock_stream_connect(unsigned int cid, unsigned int port)
{
 return vsock_connect(cid, port, SOCK_STREAM);
}

int vsock_seqpacket_connect(unsigned int cid, unsigned int port)
{
 return vsock_connect(cid, port, SOCK_SEQPACKET);
}

/* Listen on <cid, port> and return the file descriptor. */
static int vsock_listen(unsigned int cid, unsigned int port, int type)
{
 int fd;

 fd = vsock_bind(cid, port, type);

 if (listen(fd, 1) < 0) {
  perror("listen");
  exit(EXIT_FAILURE);
 }

 return fd;
}

/* Listen on <cid, port> and return the first incoming connection.  The remote
 * address is stored to clientaddrp.  clientaddrp may be NULL.
 */

int vsock_accept(unsigned int cid, unsigned int port,
   struct sockaddr_vm *clientaddrp, int type)
{
 union {
  struct sockaddr sa;
  struct sockaddr_vm svm;
 } clientaddr;
 socklen_t clientaddr_len = sizeof(clientaddr.svm);
 int fd, client_fd, old_errno;

 fd = vsock_listen(cid, port, type);

 control_writeln("LISTENING");

 timeout_begin(TIMEOUT);
 do {
  client_fd = accept(fd, &clientaddr.sa, &clientaddr_len);
  timeout_check("accept");
 } while (client_fd < 0 && errno == EINTR);
 timeout_end();

 old_errno = errno;
 close(fd);
 errno = old_errno;

 if (client_fd < 0)
  return client_fd;

 if (clientaddr_len != sizeof(clientaddr.svm)) {
  fprintf(stderr, "unexpected addrlen from accept(2), %zu\n",
   (size_t)clientaddr_len);
  exit(EXIT_FAILURE);
 }
 if (clientaddr.sa.sa_family != AF_VSOCK) {
  fprintf(stderr, "expected AF_VSOCK from accept(2), got %d\n",
   clientaddr.sa.sa_family);
  exit(EXIT_FAILURE);
 }

 if (clientaddrp)
  *clientaddrp = clientaddr.svm;
 return client_fd;
}

int vsock_stream_accept(unsigned int cid, unsigned int port,
   struct sockaddr_vm *clientaddrp)
{
 return vsock_accept(cid, port, clientaddrp, SOCK_STREAM);
}

int vsock_stream_listen(unsigned int cid, unsigned int port)
{
 return vsock_listen(cid, port, SOCK_STREAM);
}

int vsock_seqpacket_accept(unsigned int cid, unsigned int port,
      struct sockaddr_vm *clientaddrp)
{
 return vsock_accept(cid, port, clientaddrp, SOCK_SEQPACKET);
}

/* Transmit bytes from a buffer and check the return value.
 *
 * expected_ret:
 *  <0 Negative errno (for testing errors)
 *   0 End-of-file
 *  >0 Success (bytes successfully written)
 */

void send_buf(int fd, const void *buf, size_t len, int flags,
       ssize_t expected_ret)
{
 ssize_t nwritten = 0;
 ssize_t ret;

 timeout_begin(TIMEOUT);
 do {
  ret = send(fd, buf + nwritten, len - nwritten, flags);
  timeout_check("send");

  if (ret == 0 || (ret < 0 && errno != EINTR))
   break;

  nwritten += ret;
 } while (nwritten < len);
 timeout_end();

 if (expected_ret < 0) {
  if (ret != -1) {
   fprintf(stderr, "bogus send(2) return value %zd (expected %zd)\n",
    ret, expected_ret);
   exit(EXIT_FAILURE);
  }
  if (errno != -expected_ret) {
   perror("send");
   exit(EXIT_FAILURE);
  }
  return;
 }

 if (ret < 0) {
  perror("send");
  exit(EXIT_FAILURE);
 }

 if (nwritten != expected_ret) {
  if (ret == 0)
   fprintf(stderr, "unexpected EOF while sending bytes\n");

  fprintf(stderr, "bogus send(2) bytes written %zd (expected %zd)\n",
   nwritten, expected_ret);
  exit(EXIT_FAILURE);
 }
}

/* Receive bytes in a buffer and check the return value.
 *
 * expected_ret:
 *  <0 Negative errno (for testing errors)
 *   0 End-of-file
 *  >0 Success (bytes successfully read)
 */

void recv_buf(int fd, void *buf, size_t len, int flags, ssize_t expected_ret)
{
 ssize_t nread = 0;
 ssize_t ret;

 timeout_begin(TIMEOUT);
 do {
  ret = recv(fd, buf + nread, len - nread, flags);
  timeout_check("recv");

  if (ret == 0 || (ret < 0 && errno != EINTR))
   break;

  nread += ret;
 } while (nread < len);
 timeout_end();

 if (expected_ret < 0) {
  if (ret != -1) {
   fprintf(stderr, "bogus recv(2) return value %zd (expected %zd)\n",
    ret, expected_ret);
   exit(EXIT_FAILURE);
  }
  if (errno != -expected_ret) {
   perror("recv");
   exit(EXIT_FAILURE);
  }
  return;
 }

 if (ret < 0) {
  perror("recv");
  exit(EXIT_FAILURE);
 }

 if (nread != expected_ret) {
  if (ret == 0)
   fprintf(stderr, "unexpected EOF while receiving bytes\n");

  fprintf(stderr, "bogus recv(2) bytes read %zd (expected %zd)\n",
   nread, expected_ret);
  exit(EXIT_FAILURE);
 }
}

/* Transmit one byte and check the return value.
 *
 * expected_ret:
 *  <0 Negative errno (for testing errors)
 *   0 End-of-file
 *   1 Success
 */

void send_byte(int fd, int expected_ret, int flags)
{
 static const uint8_t byte = 'A';

 send_buf(fd, &byte, sizeof(byte), flags, expected_ret);
}

/* Receive one byte and check the return value.
 *
 * expected_ret:
 *  <0 Negative errno (for testing errors)
 *   0 End-of-file
 *   1 Success
 */

void recv_byte(int fd, int expected_ret, int flags)
{
 uint8_t byte;

 recv_buf(fd, &byte, sizeof(byte), flags, expected_ret);

 if (byte != 'A') {
  fprintf(stderr, "unexpected byte read 0x%02x\n", byte);
  exit(EXIT_FAILURE);
 }
}

/* Run test cases.  The program terminates if a failure occurs. */
void run_tests(const struct test_case *test_cases,
        const struct test_opts *opts)
{
 int i;

 for (i = 0; test_cases[i].name; i++) {
  void (*run)(const struct test_opts *opts);
  char *line;

  printf("%d - %s...", i, test_cases[i].name);
  fflush(stdout);

  /* Full barrier before executing the next test.  This
 * ensures that client and server are executing the
 * same test case.  In particular, it means whoever is
 * faster will not see the peer still executing the
 * last test.  This is important because port numbers
 * can be used by multiple test cases.
 */

  if (test_cases[i].skip)
   control_writeln("SKIP");
  else
   control_writeln("NEXT");

  line = control_readln();
  if (control_cmpln(line, "SKIP"false) || test_cases[i].skip) {

   printf("skipped\n");

   free(line);
   continue;
  }

  control_cmpln(line, "NEXT"true);
  free(line);

  if (opts->mode == TEST_MODE_CLIENT)
   run = test_cases[i].run_client;
  else
   run = test_cases[i].run_server;

  if (run)
   run(opts);

  printf("ok\n");
 }
}

void list_tests(const struct test_case *test_cases)
{
 int i;

 printf("ID\tTest name\n");

 for (i = 0; test_cases[i].name; i++)
  printf("%d\t%s\n", i, test_cases[i].name);

 exit(EXIT_FAILURE);
}

static unsigned long parse_test_id(const char *test_id_str, size_t test_cases_len)
{
 unsigned long test_id;
 char *endptr = NULL;

 errno = 0;
 test_id = strtoul(test_id_str, &endptr, 10);
 if (errno || *endptr != '\0') {
  fprintf(stderr, "malformed test ID \"%s\"\n", test_id_str);
  exit(EXIT_FAILURE);
 }

 if (test_id >= test_cases_len) {
  fprintf(stderr, "test ID (%lu) larger than the max allowed (%lu)\n",
   test_id, test_cases_len - 1);
  exit(EXIT_FAILURE);
 }

 return test_id;
}

void skip_test(struct test_case *test_cases, size_t test_cases_len,
        const char *test_id_str)
{
 unsigned long test_id = parse_test_id(test_id_str, test_cases_len);
 test_cases[test_id].skip = true;
}

void pick_test(struct test_case *test_cases, size_t test_cases_len,
        const char *test_id_str)
{
 static bool skip_all = true;
 unsigned long test_id;

 if (skip_all) {
  unsigned long i;

  for (i = 0; i < test_cases_len; ++i)
   test_cases[i].skip = true;

  skip_all = false;
 }

 test_id = parse_test_id(test_id_str, test_cases_len);
 test_cases[test_id].skip = false;
}

unsigned long hash_djb2(const void *data, size_t len)
{
 unsigned long hash = 5381;
 int i = 0;

 while (i < len) {
  hash = ((hash << 5) + hash) + ((unsigned char *)data)[i];
  i++;
 }

 return hash;
}

size_t iovec_bytes(const struct iovec *iov, size_t iovnum)
{
 size_t bytes;
 int i;

 for (bytes = 0, i = 0; i < iovnum; i++)
  bytes += iov[i].iov_len;

 return bytes;
}

unsigned long iovec_hash_djb2(const struct iovec *iov, size_t iovnum)
{
 unsigned long hash;
 size_t iov_bytes;
 size_t offs;
 void *tmp;
 int i;

 iov_bytes = iovec_bytes(iov, iovnum);

 tmp = malloc(iov_bytes);
 if (!tmp) {
  perror("malloc");
  exit(EXIT_FAILURE);
 }

 for (offs = 0, i = 0; i < iovnum; i++) {
  memcpy(tmp + offs, iov[i].iov_base, iov[i].iov_len);
  offs += iov[i].iov_len;
 }

 hash = hash_djb2(tmp, iov_bytes);
 free(tmp);

 return hash;
}

/* Allocates and returns new 'struct iovec *' according pattern
 * in the 'test_iovec'. For each element in the 'test_iovec' it
 * allocates new element in the resulting 'iovec'. 'iov_len'
 * of the new element is copied from 'test_iovec'. 'iov_base' is
 * allocated depending on the 'iov_base' of 'test_iovec':
 *
 * 'iov_base' == NULL -> valid buf: mmap('iov_len').
 *
 * 'iov_base' == MAP_FAILED -> invalid buf:
 *               mmap('iov_len'), then munmap('iov_len').
 *               'iov_base' still contains result of
 *               mmap().
 *
 * 'iov_base' == number -> unaligned valid buf:
 *               mmap('iov_len') + number.
 *
 * 'iovnum' is number of elements in 'test_iovec'.
 *
 * Returns new 'iovec' or calls 'exit()' on error.
 */

struct iovec *alloc_test_iovec(const struct iovec *test_iovec, int iovnum)
{
 struct iovec *iovec;
 int i;

 iovec = malloc(sizeof(*iovec) * iovnum);
 if (!iovec) {
  perror("malloc");
  exit(EXIT_FAILURE);
 }

 for (i = 0; i < iovnum; i++) {
  iovec[i].iov_len = test_iovec[i].iov_len;

  iovec[i].iov_base = mmap(NULL, iovec[i].iov_len,
      PROT_READ | PROT_WRITE,
      MAP_PRIVATE | MAP_ANONYMOUS | MAP_POPULATE,
      -1, 0);
  if (iovec[i].iov_base == MAP_FAILED) {
   perror("mmap");
   exit(EXIT_FAILURE);
  }

  if (test_iovec[i].iov_base != MAP_FAILED)
   iovec[i].iov_base += (uintptr_t)test_iovec[i].iov_base;
 }

 /* Unmap "invalid" elements. */
 for (i = 0; i < iovnum; i++) {
  if (test_iovec[i].iov_base == MAP_FAILED) {
   if (munmap(iovec[i].iov_base, iovec[i].iov_len)) {
    perror("munmap");
    exit(EXIT_FAILURE);
   }
  }
 }

 for (i = 0; i < iovnum; i++) {
  int j;

  if (test_iovec[i].iov_base == MAP_FAILED)
   continue;

  for (j = 0; j < iovec[i].iov_len; j++)
   ((uint8_t *)iovec[i].iov_base)[j] = rand() & 0xff;
 }

 return iovec;
}

/* Frees 'iovec *', previously allocated by 'alloc_test_iovec()'.
 * On error calls 'exit()'.
 */

void free_test_iovec(const struct iovec *test_iovec,
       struct iovec *iovec, int iovnum)
{
 int i;

 for (i = 0; i < iovnum; i++) {
  if (test_iovec[i].iov_base != MAP_FAILED) {
   if (test_iovec[i].iov_base)
    iovec[i].iov_base -= (uintptr_t)test_iovec[i].iov_base;

   if (munmap(iovec[i].iov_base, iovec[i].iov_len)) {
    perror("munmap");
    exit(EXIT_FAILURE);
   }
  }
 }

 free(iovec);
}

/* Set "unsigned long long" socket option and check that it's indeed set */
void setsockopt_ull_check(int fd, int level, int optname,
     unsigned long long val, char const *errmsg)
{
 unsigned long long chkval;
 socklen_t chklen;
 int err;

 err = setsockopt(fd, level, optname, &val, sizeof(val));
 if (err) {
  fprintf(stderr, "setsockopt err: %s (%d)\n",
   strerror(errno), errno);
  goto fail;
 }

 chkval = ~val; /* just make storage != val */
 chklen = sizeof(chkval);

 err = getsockopt(fd, level, optname, &chkval, &chklen);
 if (err) {
  fprintf(stderr, "getsockopt err: %s (%d)\n",
   strerror(errno), errno);
  goto fail;
 }

 if (chklen != sizeof(chkval)) {
  fprintf(stderr, "size mismatch: set %zu got %d\n"sizeof(val),
   chklen);
  goto fail;
 }

 if (chkval != val) {
  fprintf(stderr, "value mismatch: set %llu got %llu\n", val,
   chkval);
  goto fail;
 }
 return;
fail:
 fprintf(stderr, "%s val %llu\n", errmsg, val);
 exit(EXIT_FAILURE);
;
}

/* Set "int" socket option and check that it's indeed set */
void setsockopt_int_check(int fd, int level, int optname, int val,
     char const *errmsg)
{
 int chkval;
 socklen_t chklen;
 int err;

 err = setsockopt(fd, level, optname, &val, sizeof(val));
 if (err) {
  fprintf(stderr, "setsockopt err: %s (%d)\n",
   strerror(errno), errno);
  goto fail;
 }

 chkval = ~val; /* just make storage != val */
 chklen = sizeof(chkval);

 err = getsockopt(fd, level, optname, &chkval, &chklen);
 if (err) {
  fprintf(stderr, "getsockopt err: %s (%d)\n",
   strerror(errno), errno);
  goto fail;
 }

 if (chklen != sizeof(chkval)) {
  fprintf(stderr, "size mismatch: set %zu got %d\n"sizeof(val),
   chklen);
  goto fail;
 }

 if (chkval != val) {
  fprintf(stderr, "value mismatch: set %d got %d\n", val, chkval);
  goto fail;
 }
 return;
fail:
 fprintf(stderr, "%s val %d\n", errmsg, val);
 exit(EXIT_FAILURE);
}

static void mem_invert(unsigned char *mem, size_t size)
{
 size_t i;

 for (i = 0; i < size; i++)
  mem[i] = ~mem[i];
}

/* Set "timeval" socket option and check that it's indeed set */
void setsockopt_timeval_check(int fd, int level, int optname,
         struct timeval val, char const *errmsg)
{
 struct timeval chkval;
 socklen_t chklen;
 int err;

 err = setsockopt(fd, level, optname, &val, sizeof(val));
 if (err) {
  fprintf(stderr, "setsockopt err: %s (%d)\n",
   strerror(errno), errno);
  goto fail;
 }

  /* just make storage != val */
 chkval = val;
 mem_invert((unsigned char *)&chkval, sizeof(chkval));
 chklen = sizeof(chkval);

 err = getsockopt(fd, level, optname, &chkval, &chklen);
 if (err) {
  fprintf(stderr, "getsockopt err: %s (%d)\n",
   strerror(errno), errno);
  goto fail;
 }

 if (chklen != sizeof(chkval)) {
  fprintf(stderr, "size mismatch: set %zu got %d\n"sizeof(val),
   chklen);
  goto fail;
 }

 if (memcmp(&chkval, &val, sizeof(val)) != 0) {
  fprintf(stderr, "value mismatch: set %ld:%ld got %ld:%ld\n",
   val.tv_sec, val.tv_usec, chkval.tv_sec, chkval.tv_usec);
  goto fail;
 }
 return;
fail:
 fprintf(stderr, "%s val %ld:%ld\n", errmsg, val.tv_sec, val.tv_usec);
 exit(EXIT_FAILURE);
}

void enable_so_zerocopy_check(int fd)
{
 setsockopt_int_check(fd, SOL_SOCKET, SO_ZEROCOPY, 1,
        "setsockopt SO_ZEROCOPY");
}

void enable_so_linger(int fd, int timeout)
{
 struct linger optval = {
  .l_onoff = 1,
  .l_linger = timeout
 };

 if (setsockopt(fd, SOL_SOCKET, SO_LINGER, &optval, sizeof(optval))) {
  perror("setsockopt(SO_LINGER)");
  exit(EXIT_FAILURE);
 }
}

static int __get_transports(void)
{
 char buf[KALLSYMS_LINE_LEN];
 const char *ksym;
 int ret = 0;
 FILE *f;

 f = fopen(KALLSYMS_PATH, "r");
 if (!f) {
  perror("Can't open " KALLSYMS_PATH);
  exit(EXIT_FAILURE);
 }

 while (fgets(buf, sizeof(buf), f)) {
  char *match;
  int i;

  assert(buf[strlen(buf) - 1] == '\n');

  for (i = 0; i < TRANSPORT_NUM; ++i) {
   if (ret & BIT(i))
    continue;

   /* Match should be followed by '\t' or '\n'.
 * See kallsyms.c:s_show().
 */

   ksym = transport_ksyms[i];
   match = strstr(buf, ksym);
   if (match && isspace(match[strlen(ksym)])) {
    ret |= BIT(i);
    break;
   }
  }
 }

 fclose(f);
 return ret;
}

/* Return integer with TRANSPORT_* bit set for every (known) registered vsock
 * transport.
 */

int get_transports(void)
{
 static int tr = -1;

 if (tr == -1)
  tr = __get_transports();

 return tr;
}

Messung V0.5
C=95 H=88 G=91

¤ Dauer der Verarbeitung: 0.14 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge