UNIXwork

Unbekannter Terminal-Typ

04. Dezember 2016

Beim Zugriff über ssh auf ein etwas älteres Unix wie z.B. Solaris 10 erhält man in vi oder manchen anderen Terminal-Anwendungen eine Fehlermeldung wie xterm-256color: Unknown terminal type Die Anwendung ist dann meistens nicht oder nur eingeschränkt benutzbar.

Die triviale Lösung ist einfach die TERM-Umgebungsvariable auf xterm zu setzen. Wenn man das nicht immer manuell machen will, kann man das einfach in der .profile Datei (oder .bash_profile, wenn die Login-Shell die bash ist) folgendes hinzufügen:

if [ $TERM = "xterm-256color" ]; then
	TERM=xterm
	export TERM
fi

Wenn man root-Rechte auf dem Server hat, gibt es auch noch eine andere Lösung. Es gibt eine Terminfo-Datenbank, die für die verschiedenen Terminal-Typen die Fähigkeiten enthält. Den fehlenden Eintrag für das xterm-256color Terminal kann man einfach hinzufügen.

Unter Solaris 10 finden sich die Terminfo-Dateien unter /usr/share/lib/terminfo. Dort gibt es für jeden möglichen Anfangsbuchstaben ein Verzeichnis, die xterm Einträge sind daher unter /usr/share/lib/terminfo/x. Dort muss eine Datei xterm-256color rein. Die ganz einfache Lösung wäre einfach die xterm-Datei zu kopieren. Ich hab hingegen die xterm-256color-Datei von einem Solaris 11 dort eingefügt. Beides funktioniert und sobald diese Datei da ist, erkennen Anwendungen auch das xterm-256color-Terminal.

Autor: Olaf | 0 Kommentare | Tags: unix, solaris, shell

Mehrere Wege führen zur FILE

15. September 2016

Die libc-Streams, repräsentiert durch FILE-Pointer, können nicht nur mit fopen erzeugt werden. Eine ganze Reihe an Funktionen erstellt ebenfalls diese Streams, die dann auch mit den stdio-Funktionen wie z.B. fprintf genutzt werden können.

fdopen

FILE *fdopen(int fd, const char *mode);

Mit fdopen erstellt man aus einem Unix-Filedescriptor ein FILE. Diese Funktion steht unter jedem POSIX-kompatiblen Betriebsystem zur Verfügung.

fmemopen

FILE *fmemopen(void *buf, size_t size, const char *mode);

Diese Funktion erstellt aus einem Buffer ein Stream. Der Buffer hat eine feste Größe, und gelangt der Stream an dessen Ende, wird nicht weiter geschrieben. Diese Funktion wurde durch die glibc eingeführt. Sie ist mitlerweile zwar im Posix-Standard, aber noch nicht überall verfügbar.

open_memstream

FILE *open_memstream(char **bufp, size_t *sizep);

Wie bei fmemopen erhält man mit dieser Funktion einen Stream für den Zugriff auf einen Buffer, allerdings wird dieser dynamisch mit malloc erstellt. Wenn nötig wird der Buffer auch automatisch vergrößert. Auch diese Funktion ist mitlerweile Posix-Standardisiert.

Beispiel:

#define _GNU_SOURCE

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv) {
	char *buf;
	size_t len;

	FILE *f = open_memstream(&buf, &len);

	fprintf(f, "test\n");
	fprintf(f, "%d\n", 5);
	fprintf(f, "%s\n", "hello world");

	fclose(f);

	printf("%s", buf);
	free(buf);

	return 0;
}

funopen (BSD)

FILE *
funopen(void *cookie, int (*readfn)(void *, char *, int),
	int (*writefn)(void *, const char *, int),
	off_t (*seekfn)(void *, off_t, int), int (*closefn)(void *));

Die BSDs haben die sehr mächtige Funktion funopen, die einen Stream anhand eigener I/O-Funktionen erstellt. Die I/O-Operationen, die durch die libc-Funktionen, wie z.B. fgetc, fputc, fprintf, usw., erzeugt werden, werden dann an die eigenen I/O-Funktionen, die man funopen übergeben hat, weitergeleitet. So kann man für praktisch alles was man sich vorstellen kann ein FILE*-kompatibles Interfaces erstellen.

Diese Funktion ist nur unter den BSD-Betriebsystemen verfügbar. NetBSD hat sogar die noch mächtigere Funktion funopen2.

fopencookie (glibc)

FILE *fopencookie(void *cookie, const char *mode,
                     cookie_io_functions_t io_funcs);

Die glibc folgt dem BSD-Vorbild, leider waren sie natürlich nicht in der Lage, das bereits bestehende Interface zu übernehmen. Trotzdem macht fopencookie genau das gleiche wie funopen. Man erzeugt einen Stream mit eigenen I/O-Funktionen. Der Unterschied ist hier, dass die Funktionspointer nicht einzelnd übergeben werden, sondern es wird eine Struct, die die Pointer enthält, übergeben.

Autor: Olaf | 0 Kommentare | Tags: c, unix

Codedump 0

23. August 2016

Beispielcode ohne großartige Erklärung.

stdoutfilter.c

In dem Programm wird die Ausgabe von stdout durch ein Log-Prefix in jeder Zeile ergänzt.

Autor: Olaf | 0 Kommentare | Tags: c, unix

Externes Programm mit stdin/stdout

22. August 2016

Um in C ein externes Programm auszuführen, und dabei auf die Standardein- und Ausgabe des Programms zugreifen, muss man dies per Hand mit ein paar Syscalls machen. Es gibt zwar auch die Funktion popen, aber damit kann man nur in stdin des Programms schreiben, oder stdout lesen.

Programme werden in der Unixwelt gestartet, in dem mit fork der aktuelle Prozess kopiert wird. In dem neuen Kind-Prozess wird dann mit exec das gewünschte Programm ausgeführt.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

int main(int argc, char **argv) {
	pid_t pid = fork();
	if(pid == 0) {
	    // child
	    
	    return execl("/usr/bin/echo", "echo", "hello world");
	} else if(pid > 0) {
	    // parent
	    
	    int status = -1;
	    waitpid(pid, &status, 0); // wait for child termination
	} else {
	    perror("fork");
	    return EXIT_FAILURE;
	}

	return EXIT_SUCCESS;
}

Dies ist ein einfaches Grundgerüst, um ein Programm zu starten. Jetzt müssen wir noch stdin, stdout und stderr im Kind-Prozess umleiten. Dafür werden 3 Pipes benötigt, die vor dem Fork erstellt werden müssen.

int newin[2];
int newout[2];
int newerr[2];

if(pipe(newin) || pipe(newout) || pipe(newerr)) {
	perror("pipe");
	return EXIT_FAILURE;
}

Der Syscall pipe liefert uns 2 File-Deskriptoren, aus dem 1. im Array kann gelesen und in den 2. geschrieben werden. Nach dem Fork stehen die Pipes in beiden Prozessen zur Verfügung. Was jetzt noch fehlt ist, dass im Kind-Prozess die Filedeskriptoren für stdin, stdout und stderr auf die Pipe-Filedeskriptoren zeigen. Dies erledigen wir mit dup2. Dies kopiert unsere Pipe-Filedeskriptoren und ersetzt damit stdin, stdout und stderr im Kind-Prozess.

// child

// we need stdin, stdout and stderr refer to the previously
// created pipes
if(dup2(newin[0], STDIN_FILENO) == -1) {
	perror("dup2");
	return EXIT_FAILURE;
}
if(dup2(newout[1], STDOUT_FILENO) == -1) {
	perror("dup2");
	return EXIT_FAILURE;
}
if(dup2(newerr[1], STDERR_FILENO) == -1) {
	perror("dup2");
	return EXIT_FAILURE;
}

Danach würde im Kind-Prozess ein einfaches printf("Hello World!\n"); in unserer Pipe landen und könnte im Parent aus der Pipe gelesen werden.

Jetzt fehlt nur noch eine Kleinigkeit. Durch fork wurden alle Filedeskriptoren kopiert. Einige davon müssen geschlossen werden, weil sonst das Ende der Streams nicht erreicht werden könnte.

Im Kind-Prozess:

close(newin[1]);

Im Eltern-Prozess:

close(newout[1]);
close(newerr[1]);

Den kompletten Beispielcode gibt es hier. Es wird dort das Programm bc aufgerufen, die Standardeingabe davon befüllt und danach die die Ausgabe des Programms gelesen.

Autor: Olaf | 0 Kommentare | Tags: unix, c

Linkdump

20. März 2016
Autor: Olaf | 0 Kommentare | Tags: links, unix, postgresql
Zurück Weiter