Kategorie: Linux


CCC Anfang März

19. Januar 2011 - 07:30 Uhr

Anfang März ist wieder der CCC in Göttingen. Nein, nicht dieser schlechte abgekupfter Chaos Computer Congress, sondern das Original!

Der Computer Crash Course der Physik-Fachschaft zeigt den Ersties in deren ersten „Ferien“, wie man so einen Linux-Rechner vernünftig bedient. Bash, LaTeX, Gnuplot – alles, was man für’s Grundpraktikum braucht, ist dabei.

Nach derzeitigem Plan bin ich gleich mit drei Vorträgen an Bord: Der Vortrag-Vortrag, und zusammen mit Martin am Freitag dann vim und Reguläre Ausdrücke.

Vortrag-Vortrag:
Wie halte ich einen Vortrag? Wie schnell rede ich, wo kucke ich hin, klicke ich bloß Folien durch, schreibe ich vielleicht mal was an, wiederhole ich mal was, beantworte ich die Frage, sage ich, dass man ruhig welche stellen könne, wie bereite ich mich vor – und vor allem: kann man mit LaTeX auch Folien machen?

vim (vi improved):
Will man programmieren oder TeXen, braucht man einen Editor. Um Textdateien zu ändern. Da gibt es eine ganze Menge, manche sind sehr speziell, andere eher allgemein. Aber einer ist vor allem eines: Ausgeklügelt und ziemlich schnell, wenn man an bestimmte Stellen springen möchte. Das ist der vim, der „impruufte“ vi (sprich: „wim“, „wi-ai“).

Reguläre Ausdrücke:
Reguläre Sprachen stammen aus der theoretischen Informatik, speziell der Automatentheorie. Dort fragt man sich:
„Welche (interessanten) formalen Grammatiken / Sprachen können wir definieren, und wie kompliziert sind sie zu beschreiben / zu entscheiden?“

Reguläre Sprachen sind dabei die einfachsten, können durch deterministische endliche Automaten erkannt werden – und Reguläre Ausdrücke sind „Patterns“, die in dieser Grammatik verfasst werden. Diese Muster können wir dann in Texten suchen, etwa nach dem Motto: „Gib mir alle Stellen, die aus einer Zahl, einem Komma, zwei Zahlen, einem Leerzeichen und einem Euro-Zeichen bestehen“ (== Preise).

Mit spezialisierten Programmiersprachen wie AWK oder Perl kann man sehr einfach nach solchen Mustern suchen und darauf operieren (zum Beispiel Währungen umrechnen, Messdaten kombinieren, einfaches numerisches Integrieren oä).

Der CCC startet am Dienstag, 1. März, und endet am Freitag, 4. März. Mehr beim FSR Physik.

Kommentare deaktiviert für CCC Anfang März | Allgemein, Linux, Studieren in Göttingen

:TOhtml

30. Dezember 2010 - 20:13 Uhr

„Wie hast Du denn den Quellcode so schön formatiert?“, hätte man gut fragen können. Hat zwar keiner gemacht, aber hier trotzdem die Antwort:

$ source-highlight -n -i waffel.c -o waffel.html -s c.lang

Dieses nette Progrämmchen macht bunt und fett, was Quellcode ist. Mit -n gibt’s dann auch gleich die Zeilennummern vorne vor.

Das tolle: man kann nicht nur HTML-Dateien erstellen, sondern auch LaTeX und einige mehr.

Und vor wenigen Minuten fand ich noch folgendes:

:TOhtml

im vim, dem besten Editor auf der Welt. Damit ist es möglich, die aktuelle Ansicht im vim (inklusive Zeilennummern, Syntax-Farbe und Folding) als HTML-Datei zu speichern. Die ganze Datei, oder der markierte Teil (think visual).

Mehr dazu gibt es bei techspeak.

Kommentare deaktiviert für :TOhtml | Allgemein, Linux

UDP vs. TCP

30. Dezember 2010 - 08:42 Uhr

Die Netzwerkschnittstelle von unserem Waffelstrichlistenprogramm, <tt>waffel.c</tt>, basiert auf UDP. Warum aber nicht TCP?

Von der Funktion her sind beide Protokolle gleich: Im ISO-OSI-Modell entsprechen sie der Ebene Nummer 4 (Transport-Layer, zwischen 3: Network Layer und 5: Session Layer).

Sehen wir uns mal die Vorteile von TCP, dem Transmission Control Protocol, an:

  • Drei-Wege-Handschlag zum Aufbau einer Verbindung
  • Datenpakete werden in korrekter Reihenfolge zusammengesetzt
  • fehlende Pakete werden neu angefordert
  • bei Verstopfungen wird die Paketrate automatisch gedrosselt

All diese Vorteile bietet UDP, das User Datagram Protocol nicht; warum haben wir uns trotzdem dafür entschieden? Schließlich is es ja…

  • unzuverlässig,
  • ungeordnet,
  • fehleranfällig.

Aber: es ist wesentlich einfacher zu handhaben und schneller. Gerade für kurze Nachrichten (einige hundert Bytes), die sowieso nicht auf einzelne Pakete aufgeteilt werden. Das Fehlen von einem Handschlag und der Überprüfung, ob die Pakete wirklich angekommen sind, ist eine große Zeitersparnis, wenn die Nachrichten sehr kurz sind. Da verloren gegangene Pakete nicht neu angefordert werden, wird UDP gerne für Realtime-Anwendungen (Videokonferenz, VoIP etc) genutzt.

Vergleichbar mit einem Klavierspieler, der an Weihnachten begleitet und vom Blatt spielt: Trifft mal ein Finger nicht die richtige Taste – einfach weiterspielen, sonst kommen die Sänger ja aus dem Rhythmus…

Kommentare deaktiviert für UDP vs. TCP | Allgemein, Linux

set term dumb

29. Dezember 2010 - 17:22 Uhr

Und so sieht ein Konsolen-Plot in Gnuplot aus:

set term dumb

Besonders hervorzuheben ist die Waffel-Verkaufspause wegen des Feueralarms (nein, wir waren es nicht).

Kommentare deaktiviert für set term dumb | Allgemein, Linux

void* udplisten(void*);

29. Dezember 2010 - 13:36 Uhr

Die Waffel-Strichliste bietet auch eine Netzwerkschnittstelle; darüber lassen sich live wichtige Informationen abrufen:

  • Wie viele Waffeln wurden bereits verkauft?
  • Welches Lied wird gerade gespielt?
  • Wie hoch ist die (geschätzte) Spendensumme?
  • Wann wurde die letzte Waffel verkauft?

Das ganze läuft über UDP, das User Datagram Protocol. Die Funktion udp_listener wird in einem eigenen Thread gestartet. Aber zunächst die Signatur:


027: void* udplisten(void*);

Dieser Prototyp ist im Wesentlichen dadurch bestimmt, dass wir einen Funktionszeiger für die Posix-Funktion pthread_create benötigen, siehe gleich.

In der main-Funktion braucht es noch zwei Variablen:


046:     pthread_t udplistener;
047:     int udplistener_rv;

Und hier geht’s ab: Wir erstellen einen neuen Thread, sodass recvfrom() (Zeile 332) nicht den normalen Benutzerbetrieb stört.


069:     udplistener_rv = pthread_create(&udplistener, NULL, udplisten, NULL);

So, genug der Vorrede, jetzt die Funktion. Wir beginnen mit Deklarationen und sowas…


292: void* udplisten(void* nueksch)
293: {
294:     int sock;
295:     struct sockaddr_in echoserver;
296:     struct sockaddr_in echoclient;
297:     char buffer[BUFFSIZE];
298:     unsigned int clientlen, serverlen;
299:     int received = 0;
300:     char* answer;
301: 
302:     answer = (char*) malloc(sizeof(char) * BUFFSIZE);
303:     if (answer == NULL)
304:     {
305:         fprintf(stderr, "malloc failed @ line %d\n", __LINE__);
306:         exit(-1);
307:     }

Nun legen wir einen UDP-Socket an:


309:     /* Create the UDP socket */
310:     if ((sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
311:     {
312:         fprintf(stderr, "Failed to create socket\n");
313:     }

Da, wo ich diesen Schnipsel herkopiert hab, wird nun die sockaddr_in-Struktur initialisiert; im Wesentlichen vereinbaren wir einen IPv4-Socket und setzen den Port auf PORT==1042


314:     /* Construct the server sockaddr_in structure */
315:     memset(&echoserver, 0, sizeof(echoserver));       /* Clear struct */
316:     echoserver.sin_family = AF_INET;                  /* Internet/IP */
317:     echoserver.sin_addr.s_addr = htonl(INADDR_ANY);   /* Any IP address */
318:     echoserver.sin_port = htons(PORT);                /* server port */

Mit bind() aktivieren wir nun unseren Server-Socket:


320:     /* Bind the socket */
321:     serverlen = sizeof(echoserver);
322:     if (bind(sock, (struct sockaddr *) &echoserver, serverlen) < 0)
323:     {
324:         fprintf(stderr, "Failed to bind server socket\n");
325:     }

Endlosschleife: warte auf eine Anfrage und bearbeite sie…


327:     /* Run until cancelled */
328:     while (1)
329:     {
330:         /* Receive a message from the client */
331:         clientlen = sizeof(echoclient);
332:         if ((received = recvfrom(sock, buffer, BUFFSIZE, 0,
333:                         (struct sockaddr *) &echoclient,
334:                         &clientlen)) < 0)
335:         {
336:             fprintf(stderr, "Failed to receive message\n");
337:         }

Damit die Anfrage auch schön NULL-terminiert ist:


338:         buffer[received] = '';

Die Anfragen bestehen aus einem string, der folgenden Text enthält:

  • qWaffelCount
  • qSpendenSumme
  • qLastWaffel
  • qSong

Die Anfrage steht in buffer und wird mittels strcmp überprüft; dann wird mit sprintf die Antwort zusammengeschustert:


340:         sprintf(answer, "---");
341:         if (strcmp(buffer, "qWaffelCount") == 0)
342:             sprintf(answer, "Bisher verkaufte Waffeln: %d\n", qWaffelCount());
343:         if (strcmp(buffer, "qSpendenSumme") == 0)
344:             sprintf(answer, "Aktuelle Spendensumme: %.2f Euro\n", 0.42*qWaffelCount());
345:         if (strcmp(buffer, "qLastWaffel") == 0)
346:             sprintf(answer, "Letzte Waffel: %s", asctime(qLastWaffel()));

Das aktuelle Musikstück kennt mocp, zu dem wir eine Pipe aufbauen:


347:         if (strcmp(buffer, "qSong") == 0)
348:         {
349:             FILE* fpipe;
350:             char* command="mocp -i | grep ^Title";
351:             char line[256];
352: 
353:             if ( !(fpipe = (FILE*)popen(command, "r")) )
354:             {
355:                 perror("Problems with pipe");
356:                 exit(1);
357:             }
358: 
359:             while ( fgets( line, sizeof line, fpipe))
360:             {
361:                 sprintf(answer, "Aktuelles Lied: %s", line);
362:             }
363:             pclose(fpipe);
364:         }

Bevor es funktionierte, gab es hier eine Ausgabe:


366:         /*
367:         fprintf(stderr, "Client connected: %s\n", inet_ntoa(echoclient.sin_addr));
368:         fprintf(stderr, "Q: %s\n", buffer);
369:         fprintf(stderr, "A: %s\n", answer);
370:         */

Mit sendto schicken wir die Antwort zurück, dann geht die Schleife wieder von vorn los.


372:         /* Send the message back to client */
373:         sendto(sock, answer, strlen(answer), 0, (struct sockaddr *) &echoclient, sizeof(echoclient));
374:     }
375: 
376:     return NULL;
377: }

Kommentare deaktiviert für void* udplisten(void*); | Allgemein, Linux

int show_plot();

22. Dezember 2010 - 18:42 Uhr

Nicht sonderlich spektakulär: von C aus gnuplot aufrufen…


00429: int show_plot()
00430: {
00431:     return system("gnuplot < gnu.plot");
00432: }

Das Plotfile hingegen bietet da schon mehr Einsichten:


00001: set term dumb 125 45
00002: set xlabel 'Uhrzeit'
00003: set ylabel 'Anzahl'
00004: set xdata time
00005: set timefmt "%s"
00006: set format x "%H:%M:%S"
00007: plot [] [0:11] 'waffel.input' u ($2+3600):($1 > 0 ? $1 : 1/0) w points ps 5 pt 3 t '15.12.10'

Das Terminal dumb ist gar nicht doof, sondern die Konsole. Dort wird auf 125 mal 45 Zeichen (fast) jede verkaufte Waffel durch ein W dargestellt; genauer: die Anzahl der auf einmal verkauften Waffeln als Funktion der Zeit. Um Fehleingaben und negative Korrekturwaffeln zu unterdrücken, ist die y-Achse auf 0 bis 10 eingeschränkt.

Die +3600 Sekunden sind übrigens Winterzeit gegen Greenwich.

Kommentare deaktiviert für int show_plot(); | Allgemein, Linux

int waffelloop(FILE*)

21. Dezember 2010 - 15:24 Uhr

Heute möchten wir uns die Waffel-Schleife von waffel2010 ansehen.

Zunächst noch einmal die Signatur:


00022: int waffelloop(FILE*);

waffelloop erhält also einen FILE-Pointer und gibt einen int zurück. Als FILE-Pointer übergeben wir zunächst eine geöffnete Datei:


00063:         waffelloop(infile);

In dieser ist die bisherige Geschichte gespeichert, damit wir das Programm unterbrechen und später neu starten können. Anschließend gibt die main-Funktion ein bisschen Text aus und startet die waffelloop mit einem Zeiger auf stdin: Nun werden die Eingaben von der Tastatur (alternativ aus einer Pipe) gelesen.


00071:     err = waffelloop(stdin);

Gehen wir doch einmal die Implementation der waffelloop durch. Zunächst deklarieren wir Variablen und allokieren Speicher für einen Eingabe-Puffer:


00133: int waffelloop(FILE* infile)
00134: {
00135:     char* buffer;
00136:     const int buffer_size = 32;
00137:     FILE* outfile = NULL;
00138: 
00139:     buffer = (char*) malloc(buffer_size * sizeof(char));
00140:     if (buffer == NULL)
00141:         return __LINE__;

Wenn die Eingabe nicht aus der der waffel.input stammt (als FILE* also stdin übergeben wurde), öffnen wir diese Datei zum Anhängen, um die Eingaben zu speichern. Kann sie nicht geöffnet werden, geben die Zeilennummer __LINE__ (gcc-Makro) als Fehler zurück:


00143:     if (infile == stdin)
00144:     {
00145:         outfile = fopen("waffel.input", "a");
00146:         if (outfile == NULL)
00147:             return __LINE__;
00148:     }

Nun beginnt die Endlos-Schleife while(1). Wir deklarieren einen Zähler int waffel_count=0 und einen Zeitstempel time_t when=0; ist die Eingabe stdin, geben wir einen Prompt auf stdout aus. Anschließend lesen wir eine Zeile in den Puffer.


00150:     while (1)
00151:     {
00152:         int waffel_count = 0;
00153:         time_t when=0;
00154: 
00155:         if (infile == stdin)
00156:             printf("Waffel $ ");
00157:         fflush(stdout);
00158:         if (fgets(buffer, buffer_size, infile) == NULL)
00159:             break;

Am Dateiende schlägt fgets fehl, wir verlassen die Schleife mit einem break.

Nun ein fieser Hack: Hat der Nutzer nur Enter gedrückt, schreiben wir 1\n in den Puffer:


00161:         if (buffer[0] == '\n')
00162:         {
00163:             buffer[0] = '1';
00164:             buffer[1] = '\n';
00165:         }

Fallunterscheidung zur Behandlung der Eingabe: lesen wir aus der waffel.input, bekommen wir die Zahl der verkauften Waffeln und den Zeitstempel.


00166:         if (infile != stdin)
00167:         {
00168:             int when_int;
00169:             sscanf(buffer, "%d %d", &waffel_count, &when_int);
00170:             when = when_int;
00171:         }

Im anderen Fall müssen wir die Benutzer-Eingabe verarbeiten. Im einfachsten Fall steht in buffer eine Zahl, die wir mittels sscanf in die Variable waffel_count schreiben können; den Zeitstempel bekommen wir von time(0):


00172:         else
00173:         {
00174:             sscanf(buffer, "%d", &waffel_count);
00175:             when = time(0);
00176:             fprintf(outfile, "%3d %d\n", waffel_count, (int)when);
00177:             fflush(outfile);
00178:         }

Hat sscanf jedoch eine 0 gelesen, vergleichen wir das erste Zeichen, buffer[0], mit den erlaubten Befehlen, und springen in die entsprechenden Funktionen (oder verlassen die while(1)-Schleife):


00180:         if (waffel_count == 0)
00181:         {
00182:             if (buffer[0] == 'x' || buffer[0] == 'X')
00183:                 break;
00184:             if (buffer[0] == 'a' || buffer[0] == 'A')
00185:                 start_alsamixer();
00186:             if (buffer[0] == 'h' || buffer[0] == 'H')
00187:                 print_help();
00188:             if (buffer[0] == '?')
00189:                 print_help();
00190:             if (buffer[0] == 's' || buffer[0] == 'S')
00191:                 output_uptime();
00192:             if (buffer[0] == 'l' || buffer[0] == 'L')
00193:                 output_last_waffel();
00194:             if (buffer[0] == 'n' || buffer[0] == 'N')
00195:                 blubber = system("mocp --next");
00196:             if (buffer[0] == 'p' || buffer[0] == 'P')
00197:                 show_plot();
00198:             if (buffer[0] == 't' || buffer[0] == 'T')
00199:                 print_datetime();
00200:         }

Ansonsten wurde eine Zahl gelesen, der Waffel-Zähler entsprechend geändert und der Eintrag in die Datei waffel.input geschrieben:


00201:         else
00202:         {
00203:             save_to_history(waffel_count, when);
00204:             if (infile == stdin)
00205:             {
00206:                 if (waffel_count > 0)
00207:                     alert(waffel_count);
00208:                 printf("\n");
00209:             }
00210:         }

Nach dem Schleifenende schließen wir outfile und geben zurück.


00211:     }
00212: 
00213:     if (infile == stdin)
00214:         fclose(outfile);
00215:     
00216:     return 0;
00217: }

Anmerkungen:

  • Für nächstes Jahr wird das Programm auf die readline-Bibliothek vorbereitet; dann kann man nämlich die Eingabe löschen.
  • Eine falsche Waffel-Zahl kann durch Eingabe einer negativen Zahl korrigiert werden.

Kommentare deaktiviert für int waffelloop(FILE*) | Allgemein, Linux

waffel.c

19. Dezember 2010 - 20:45 Uhr

Rund tausend Waffeln konnte der Fachschaftsrat in dieser Vorweihnachtszeit backen und verkaufen – der Erlös geht an die Göttinger Tafel. Und damit wir immer genau wissen, wann wie viele Waffeln verkauft werden, gibt es eine Statistik.

waffel 2010 – Quellcode hier: https://github.com/osti/kochbuch/blob/master/fsr/waffel-src/waffel.c

Die verkauften Waffeln werden in einer struct gespeichert:

time_t when; // Sekunden seit 1970 der Waffeln
unsigned int count; // Anzahl der Waffeln
void* next; // Zeiger auf die nächste Waffel (einfach verkettete Liste)

Und diese Funktionen gibt es:

int save_to_history(unsigned int, time_t); // lege Waffeln mit Zeitstempel in verketteter Liste ab
int output_time_with_text(char*); // gibt Zeit der zuletzt verkauften Waffel mit Text aus
int output_last_waffel(); // gib Zeit der zuletzt verkauften Waffel aus
int output_uptime(); // gib einen load aus (Mittelwert der verkauften Waffeln pro Minute, über die letzten 1, 5, 15 Minuten)
int waffelloop(FILE*); // die eigentliche Schleife, dazu später mehr
void alert(unsigned int); // pausiere die Musik und spiele die danke.wav ab
void mocp_start(); // starte die Musik
void mocp_stop(); // stoppe die Musik
void* udplisten(void*); // zweiter Thread für die Netzwerkschnittstelle – dazu demnächst mehr
int qWaffelCount(); // wie viele Waffeln bislang verkauft wurden
struct tm* qLastWaffel(); // Struktur auf die letzte Waffel
int print_greetings(); // begrüße die Waffelverkäufer
int print_datetime(); // gib aktuelle Uhrzeit aus
void print_help(); // kurze Anleitung ausgeben
int start_alsamixer(); // alsamixer starten zur Lautstärkeregelung
int show_plot(); // gnuplot-Plot anzeigen

Morgen schauen wir uns dann die int waffelloop(FILE*) genauer an.

Kommentare deaktiviert für waffel.c | Allgemein, Linux

./s685-getvcf.sh

5. Dezember 2010 - 18:58 Uhr

Heute mal was Praktisches:

ein kleines Skript, um das Telefonbuch von einem S685-Telefon zu laden.

Und das gibt’s hier.

(Da die code-Umgebung dieser Blog-Software doof ist, verlinkt.)

(Und weil man hier weder .sh- noch .txt-Dateien hochladen darf, bei github in mein Kochbuch gepackt.)

Per curl logt sich das Skript auf der Basisstation ein, speichert das Cookie, und lädt nacheinander alle Telefonbücher runter. Dazu gibt man in dem Array $HEADSETS die Dateinamen ein; die Headsets sind von 0 an durchnummeriert.

Anschließend kann man die .vcf-Dateien (vCard-Format) miteinander vergleichen:

meld el.vcf mo.vcf

Dabei fiel mir auch gleich auf, dass meine beiden Sabbelknochen unterschiedliche Nummern gespeichert haben.

Demnächst: ./s685-putvcf.sh

Kommentare deaktiviert für ./s685-getvcf.sh | Allgemein, Linux

gobby – endlich mit undo

11. Juli 2010 - 08:59 Uhr

Gobby ist ein kollaborativer Text-Editor. Man kann also Text-Dateien bearbeiten – aber im Team!

Per Netzwerk klinken sich die Kollegen rein, und dann geht es los. Der eine möchte in dieser Datei etwas ändern, der andere dort ein Wort austauschen; manchmal tippt man auch zu zwei gleichzeitig in einer Zeile!

Dabei werden die Zeichen farbig hinterlegt – jenachdem, wer sie zuletzt getippt hat. So wird es schnell reichlich bunt. Zur Kommunikation gibt es einen Chat, alles ist verschlüsselt und kann passwortgeschützt sein.

Und jetzt endlich, dank neuer Bibliothek: Es gibt einen „Rückgängig“-Knopf.

Läuft unter Linux, zwei berühmten Kommerziellen Systemen, und so manchem anderen Unix…

Einige Schimschüsse:

Gobby unter Linux

Gobby unter Linux

Gobby auf'm Apfel

Gobby auf

Gobby für Fenster

Gobby für Fenster

Mehr Bilder.

Das Programm.

1 Kommentar » | Allgemein, Linux

« Ältere Einträge     

Zur Werkzeugleiste springen