Shutdown: ... auch Ausschalten will gelernt sein

Manche Rechnersysteme lassen sich bequem per Software abschalten. Nach Auswählen des Befehls ‘Ausschalten’ wird der Rechner noch bestimmte Aktionen zu Ende bringen und sich dann in einen Stand-by-Modus begeben. Ganz so komfortabel geht es auf dem ATARI zwar nicht (weil die hierfür notwendige Hardware leider nicht vorhanden ist), aber immerhin wurde mit AES 4 ein entsprechender Mechanismus eingeführt, der es zumindest theoretisch ermöglicht, einen Shutdown herbeizuführen. Nur theoretisch? Auch praktisch!

Anwendungen, die das System herunterfahren oder einen Auflösungswechsel initiieren wollen, müssen hierfür shel_write() benutzen, woraufhin die AES die Nachricht AP_TERM versenden werden. Allerdings ist zwingende Voraussetzung, daß alle sich im System befindlichen AES-Prozesse kundgetan haben, daß sie den Mechanismus unterstützen. Sonst nämlich droht dem Prozeß des Shutdown bzw. Auflösungswechsels schon in recht frühem Stadium ein jähes Ende.

Wer AP_TERM unterstützen will, teilt dies den AES ebenfalls via shel_write() mit. Trifft diese Nachricht ein, sollte die Anwendung so schnell wie möglich terminieren. Selbstverständlich ist zu prüfen, ob noch Dokumente zu sichern sind, und der Benutzer gegebenenfalls zu informieren. Sieht sich die Anwendung außerstande, sich zu beenden, teilt sie das den AES mit, und zwar ebenfalls mit - der geneigte Leser hat es sicher schon vermutet - shel_write().

Im Normalfall sollte dies aber nur geschehen, wenn der Anwender dies ausdrücklich wünscht. Dies träfe zum Beispiel zu, wenn er ein Dokument noch nicht gesichert hat und entscheidet, es weiter zu bearbeiten.

Eigentlich wäre zu diesem Zeitpunkt zu dem Shutdown- bzw. Auflösungswechselmechanismus bereits alles gesagt. Die in diesem Zusammenhang wichtigen shel_write()-Modi sind - wie auch die relevanten AES-Nachrichten - dem Listing zu entnehmen. Natürlich soll nun nicht jedes Programm einen Menüpunkt ‘Shutdown’ bereitstellen. Dies ist vielmehr Aufgabe der Shells oder eben kleiner Zusatzprogramme. Was jedoch Aufgabe der Anwendungsprogramme ist, ist das richtige Reagieren auf die Nachricht AP_TERM. Zum Glück trifft dies auf immer mehr Anwendungen zu, aber leider treten in Zusammenhang mit älteren Programmen eben doch Probleme auf.

Das Steuer-CPX-Modul

Hier kommt SHUTDOWN.CPX ins Spiel. Dieses Modul für ATARIs XControl hat zwei Aufgaben: dem Benutzer die Möglichkeit zu geben, einen Shutdown/Auflösungswechsel herbeizuführen und ältere Anwendungen sozusagen von Hand aus dem Speicher zu befördern, damit nur noch Programme übrig bleiben, die den Mechanismus sowieso kennen. Leider müssen hier auch 2 Einschränkungen gemacht werden: AES 4 und MiNT sind Voraussetzung für den sinnvollen Einsatz dieses Moduls. Die Bedienung: Nach dem Aufruf kann der Anwender durch Klick auf den entsprechenden Button einen Shutdown initiieren. Wird 'Auflösungswechsel’ angeklickt, erscheint ein Pop-up-Menü, mit dem der neue Grafikmodus selektiert werden kann. Sollte die Ausführung der Funktion nicht möglich sein, erscheint ein entsprechender Hinweis. Falls MiNT geladen war und im Wurzelverzeichnis des Boot-Laufwerks die Datei SHUTDOWN.INF gefunden wurde, kann der Button ‘Kill’ angewählt werden, sonst ist er nicht anwählbar. In dieser Datei können Programme spezifiziert werden, die - sofern sie zum Zeitpunkt des Aufrufs im Speicher sind - mittels Pkill() aus dem Speicher entfernt werden. In diese Liste wird man zweckmäßigerweise alle Anwendungen eintragen, von denen man weiß, daß sie APJTERM nicht unterstützen. Die Datei läßt sich mit jedem ASCII-Editor erstellen. Um den Aufwand beim Einlesen gering zu halten, muß jede Zeile exakt 8 Zeichen enthalten und mit CR/LF abgeschlossen werden. Die 8 Buchstaben entsprechen dem Dateinamen des Programms, wobei gegebenenfalls Spaces ergänzt werden müssen.

Mit diesem CPX-Modul kann ein Shutdown-Kommando ausgelöst werden.

Interna

In der vorliegenden Fassung werden nur die 6 Auflösungen eines TT angeboten. Die zusätzlichen Modi des Falcon können aber sehr leicht eingebaut werden. Dazu sind das String Array sowie die Felder isgr[] und iscr[] entsprechend zu erweitern. In das isgr[] Feld werden Einsen eingetragen, iscr[] bekommt die Werte, die der Falcon nach einen Aufruf von Getrez() liefert. Zu beachten ist, daß Probleme mit Programmen auftreten werden, die in Systemvektoren hängen, aber nicht das Signal SIGTERM abfangen. Das Modul fordert alle Prozesse aus der Dateiliste erst mit SIGTERM auf, sich zu beenden. Später werden sie in jedem Fall durch SIGKILL aus dem Speicher entfernt. In diesem Fall bleiben aber Systemvektoren verstellt, was bei einem Shutdown keine Probleme bereitet, wohl aber bei einem Auflösungswechsel. Hier sollten die Autoren aufgefordert werden auf das Signal SIGTERM entsprechend zu reagieren.

Werden nur Anwendungen benutzt, die den Mechanismus kennen, kann auf die Datei SHUTDOWN.INF selbstverständlich verzichtet werden. (Anm. d. Red.: Aus Platzgründen haben wir nur die wichtigsten Listings abgedruckt, alle anderen und die fertig compilierten Programme und Konfigurationsdateien befinden sich auf der Megadisk zu diesem Heft.)


/*
    SHUTDOWN.CP
    (System herunterfahren und Auflosungswechsel initiieren)
    Autor: Thomas Künneth
    V1.0 ab 16.1.1995 
    (c) 1995 by MAXON Computer 
    um das Modul übersetzen zu können ist 
    die Headerdatei CPX.H erforderlich 
*/

#define EXTERN extern

/* Headerfiles */

#include "shutdown.rsh"
#include "shutdown.rh"
#include <stddef.h>
#include <cpx.h>
#include <string.h»
#include <tos.h>
#include <vdi.h>
#include <stdlib.h>
#include <stdio.h»

typedef enum { FALSE, TRUE } BOOLEAN; 

extern GEMPARBLK _GemParBlk;

/* Prototypen */
CPXXNFO * cdecl cpx_init(XCPB *Xcpb); 
int cdecl cpx_call(GRECT *rect);
int popup(OBJECT *tree,int object,char *entries[], int number_of_entries,int defanlt_entry,GRECT *r);

/* Zeiger auf die XControl-Funktionen */
XCPB *xcpb;

/* Zeiger auf unsere Funktionen */
CPXINFO cpxinfo;

/* Feld für PIDs von Prozessen, die per Pkill() entfernt werden */ 
int pid[50];

/* das Popup mit den Bildschirm-Modi */ 
char *scrmodes[] = { "  ST-niedrig ",
    "  ST-mittel ",
    "  ST-hoch ",
    "  TT-niedrig ",
    "  TT-mittel ",
    "  TT-hoch "
} ;

/* soviele Einträge hat es */
#define NUM_SCRMODES 6

/* hier werden die Parameter für shel_write() eingetragen */ 
int iscr[NUM_SCRMODES] = {0,0,0,0,0,0}; 
int isgr[NUM_SCRMODES] = {2,3,4,9,6,8} ;

int current_page, /* welcher Dialog? ‘/
xc_mint; /* MiNT-ID XControl */

int popup(OBJECT *tree,int Object,char *entries[],int number_of_entries,int default_entry,GRECT *r)
{
    GRECT pos;
    objc_offset(tree,object,&pos g_x,&pos.g_y) ;
    pos.g_w=tree[object].ob.width;
    pos.g_h=tree[object].ob_height;
    return ((*xcpb->Popup)(entries,number_of_entries, default_entry,3,&pos,r));
}

CPXINFO *cdecl cpx _init (XCPB *Xcpb) 
{
    int i;
    xcpb = Xcpb; 
    if (xcpb->booting) 
        return ((CPXINFO *)TRUE); 
    else { 
        if (!xcpb->SkipRshFix) 
            for (i = 0; i < NUM_OBS; i++)
                (xcpb->rsh_obfix)(rs_object,1); 
        cpxinfo.cpx_call = cpx_call; 
        return (Scpxinfo);
    }
}

/* im Supervisor-Mode ausführen! */
long get_bootdev(void)
{
    return ((long)*((int *)0x446));
}

void kill_procs(int num)
{
    int i;
    /* Prozesse bitten, sich zu beenden */ 
    for (i = 0; i < num; i++)
        Pkill(pid[i],SIGTERM);
    /* ein paar Sekunden Zeit lassen */
    Fselect(3000,NULL,NULL,NULL);
    /* jetzt Prozesse abschießen */ 
    for (i = 0; i < num; i + + )
        Pkill(pid[i],SIGKILL);
}

int term_procs(void)
{
    char *filename = "x:\\shutdown.inf",
        *buf; 
    long error, flen, i;
    int fd, count = -1;
    /* MiNT Cookie suchen */
    if ((xcpb->getcookie)('MiNT',NULL) != 0) {
        /* Bootlaufwerk ermitteln und eintragen */ 
        filename[0] = 65 + (char)Supexec(get_bootdev);
        /* versuchen, Datei zu öffnen */ 
        error = Fopenifilename,FO_READ); 
        if (error >= 0) { 
            fd = (int)error;
            /* Länge der Datei ermitteln */ 
            flen = Fseek(0,fd,2);
            /* Speicher reservieren und einlesen */
            Fseek(0,fd,0); 
            buf = Malloc(flen + 1); 
            if (buf != NULL) { 
                buf[flen] = '\0'; 
                if (Fread(fd,flen,buf) == flen) { 
                    i = 0; 
                    count = 0;
                    while (i <= (flen - 8)) { 
                        buf[i + 8] = '\0';
                        /* nach dieser Anwendung suchen */ 
                        pid[count] = appl_find(&(buf[i])); 
                        if (pid[count] >= 0) {
                            /* die MiNT-PID ermitteln */ 
                            pid[count] = appl_find((void *)(0xfffe0000L | (long)pid[count])); 
                            if (pid[count] != -1) { 
                                count += 1; 
                                if (count == 50) 
                                    break;
                            }
                        }
                        i += 10;
                    }
                }
                Mfree(buf);
            }
            Fclose(fd);
        }
    }
    return (count);
}

BOOLEAN ini_shutdown(int doex,int isgr,int iscr)
{
    BOOLEAN success = TRUE, first = TRUE;
try_again:
    if (shel_write(doex,isgr,iscr,NULL,NULL) == 0) { 
        if (first != FALSE) { 
            first = FALSE; 
            if (term_procs() != FALSE) 
                goto try_again;
        }
        success = FALSE;
    }
    return (success);
}

int cdecl cpx_call(GRECT *rect)
{
    int old = -1, result, xcid,
        num_procs, new_rez, mbuf[8];
    BOOLEAN abbruch = FALSE;
    OBJECT *tree;
    /* die Hauptseite zeigen */ 
    current_page = MAIN_PAGE; 
    appl_init();
    /* nur ab AES4 weitermachen */ 
    if (_GemParBlk.global[0] >= 0x400) { 
        xcid = appl find("XCONTROL");
        /* MiNT-ID XControl */
        xc_mint = appl_find( (void *) (0xfffe0000L | (long)xcid));
        /* ggf. den Kill-Button deaktivieren */ 
        num_procs = term_procs(); 
        if (num_procs > 0) 
            rs_trindex[SHUT_ERROR][BT_ED_KILL].ob_state &= ~DISABLED; 
        else
            rs_trindex[SHUT_ERROR][BT_ED_KILL].ob_state |= DISABLED; 
            while (abbruch == FALSE) { 
                if (old != current_page) { 
                    tree = rs_trindex[current_page]; 
                    tree[0].ob_x = rect->g_x; 
                    tree[0].ob_y = rect->g_y;
                    objc_draw(tree, ROOT, MAX_DEPTH, rect->g_x, rect->g_y, rect->g_w,rect->g_h); 
                    old = current__page;
                }
                result = (xcpb->Xform_do)(tree,0,mbuf); 
                if (result == -1) 
                    switch (mbuf[0]) { 
                        case AC_CLOSE: 
                        case WM_CLOSED: 
                            abbruch = TRUE; 
                            break;
                    }
                else {
                    result &= 0x7fff; /* Doppelklick ignorieren */
                    objc_change(tree,result,0,rect->g_x,rect->g_y,rect->g_w,rect->g_h,tree[result].ob_state & ~SELECTED, TRUE);
                    switch (current_page) { 
                        case SHUT_DO: 
                            if (result == BT_DOSHUT_OK) { 
                                if (ini_shutdown(4,2,0) == FALSE) 
                                    current_page = SHUT_ERROR; 
                                else 
                                    abbruch = TRUE;
                            }
                            else
                                current_page = MAIN_PAGE; 
                            break; 
                        case SHUT_ERROR: 
                            if (result == BT_ED_KILL) 
                                kill_procs(num_procs); 
                            current_page = MAIN_PAGE; 
                            break; 
                        case MAIN_PAGE: 
                            if (result == BT_MP_SHUTDOWN) 
                                current_page = SHUT_DO; 
                            else
                                if (result == BT_MP_CANCEL) 
                                    abbruch = TRUE; 
                                else
                                    if (result == PU_MP_SELRES) { 
                                        new_rez = popup(tree,PU_MP_SELRES,scrmodes, NUM_SCRMODES, -1,rect); 
                                        if (new_rez != -1) 
                                            if (ini_shutdown(5, isgr[new_rez],iscr[new_rez]) == FALSE) 
                                                current_page = SHUT_ERROR; 
                                            else { 
                                                mbuf[0] = AP_TERM; 
                                                mbuf[I] = _GemParBlk.global[2]; 
                                                mbuf[2] = 0; 
                                                mbuf[5] = AP_RESCHG; 
                                                appl_write(xcid,16,mbuf);
                                                Pkill(xc_mint,SIGTERM); 
                                                abbruch = TRUE;
                                            }
                                    }
                                    break;
                }
            }
        }
    }
    appl_exit();
    return (0);
}
/* Resource Datei Indizes für SHUTDOWN */

#define MAIN_PAGE       0 /* Formular/Dialog */
#define BT_MP_SHUTDOWN  2 /* BUTTON in Baum MAIN_PAGE */
#define PU_MP_SELRES    4 /* BUTTON in Baum MAIN_PAGE */
#define BT_MP_CANCEL   10 /* BUTTON in Baum MAIN_PAGE */

#define SHUT_ERROR      1 /* Formular/Dialog */
#define BT_ED_KILL      9 /* BUTTON in Baum SHUT_ERROR */
#define BT_ED_OK       10 /* BUTTON in Baum SHUT_ERROR */

#define SHUT_DO         2 /* Formular/Dialog */
#define BT_DOSHUT_OK    8 /* BUTTON in Baum SHUT_DO */
#define BT_DOSHUT_CAN   9 /* BUTTON in Baum SHUT_DO */

#define SHUT_DONE       3 /* Formular/Dialog */
/* GEM C Source Header Datei */

EXTERN TEDINFO  rs_tedinfo[]; 
EXTERN CICONBLK rs_ciconblk[]; 
EXTERN CICON    rs_cicon[]; 
EXTERN ICONBLK  rs_iconblk[]; 
EXTERN BITBLK   rs_bitblk[]; 
EXTERN BYTE     *rs_frstr[]; 
EXTERN BITBLK   *rs_frimg[]; 
EXTERN OBJECT   rs_object[]; 
EXTERN OBJECT   *rs_trindex[];

#define NUM_BB      3 
#define NUM_IB      0 
#define NUM_CIB     0 
#define NUM_TI      4 
#define NUM_FRSTR   0 
#define NUM_FRIMG   0 
#define NUM_OBS    36 
#define NUM_TREE    4

Thomas Künneth
Aus: ST-Computer 04 / 1995, Seite 92

Links

Copyright-Bestimmungen: siehe Über diese Seite