next up previous contents
Next: msgtool: Interaktywny manipulator kolejką Up: Kolejki wiadomości Previous: WYWOŁANIE SYSTEMOWE: msgsnd()   Contents

WYWOŁANIE SYSTEMOWE: msgctl()

Podczas tworzenia tych przykładowych funkcji poznałeś proste i eleganckie podejście do tworzenia i używania kolejek wiadomości. Teraz przedyskutujemy bezpośrednią manipulację wewnętrznych struktur związanych z daną kolejką.

Aby kontrolować kolejkę używamy wywołania systemowego msgctl().


     WYWOŁANIE SYSTEMOWE: msgctl();
     PROTOTYP: int msgctl ( int msgqid, int cmd, struct msqid_ds *buf );
       ZWRACA: 0 - sukces
                -1 - błąd: errno = EACCES ( odczyt niedozwolony a cmd jest usawione na IPC_STAT )
                                   EFAULT ( adres wskazywany przez buf jest nieprawidłowy dla
   				         komend IPC_SET i IPC_STAT )
                                   EIDRM  ( kolejka została usunięta podczas odczytu (retrieve) )
                                   EINVAL ( nieprawidłowe msgqid lub msgsz ujemne )
                                   EPERM  ( komendy IPC_SET lub IPC_RMID zostały wybrane, lecz
                                            proces nie może zapisywać lub nie ma dostępu
   					 do kolejki )
     UWAGI:
   

Logika podpowiada ci, iż bezpośrednia manipulacja wewnętrznych struktur jądra może prowadzić do nocnych igraszek. Niestety, odpowiedzialność spoczywająca na programiście może zostać zaklasyfikowana jako zabawa pod warunkiem, że lubisz zaśmiecać podsystem IPC. Wywołując msgctl() z odpowiednio dobranymi poleceniami możesz manipulować jednostki, które zbyt łatwo nie doprowadzą systemu do upadku. Przyjżyjmy się tym komendom:

IPC_STAT

Odczytuje strukturę msqid_ds z kolejki i przechowuje ją pod adresem wskazywanym przez argument buf.

IPC_SET

Ustawia wartość ipc_perm ( członka struktury msqid_ds ). Wartość pobierana jest z argumentu buf.

IPC_RMID

Usuwa kolejkę z jądra.

Przypomnij sobie naszą dyskusję na temat struktury msqid_ds. Jądro przechowuje kopię tej struktury dla każdej kolejki istniejącej w systemie. Używając polecenia IPC_STAT możemy otrzymać kopię tej struktury.

Przyjżyjmy się funkcji odbierającej i kopiującej tę strukturę pod podany adres:


   int get_queue_ds( int qid, struct msgqid_ds *qbuf )
   {
           if( msgctl( qid, IPC_STAT, qbuf) == -1)
           {
                   return(-1);
           }
           
           return(0);
   }
   

Jeżeli nie udało nam się skopiować wewnętrznego bufora zwracamy -1. Jeżeli wszystko poszło dobrze zwracamy 0, a podany bufor powinien zawierać kopię wewnętrznej struktury dla kolejki reprezentowanej przez podany identyfikator (qid).

Mamy już kopię wewnętrznej struktury dla kolejki, zdajmy sobie pytanie co możemy zmienić? Jedynym elementem, który możemy zmienić jest struktura ipc_perm. Zawiera ona prawa dostępu oraz informacje o twórcy i właścicielu kolejki. Jednakże, możemy zmienić jedynie mode, uid i gid, czyli id właściciela, id grupy, oraz prawa dostępu do kolejki.

Utwórzmy funkcję zmieniającą prawa dostępu do kolejki. Musimy je przekazać jako tablicę znaków ( np.: ``660'').


   int change_queue_mode( int qid, char *mode )
   {
           struct msqid_ds tmpbuf;
   
           /* pobierz kopię wewnętrznej struktury */
           get_queue_ds( qid, &tmpbuf);
   
           /*  zmień prawa dostępu używając starego triku */
           sscanf(mode, "%ho", &tmpbuf.msg_perm.mode);
   
           /* uaktualnij wewnętrzną strukturę */
           if( msgctl( qid, IPC_SET, &tmpbuf) == -1)
           {
                   return(-1);
           }
           
           return(0);
   }
   

Pobieramy kopię struktury za pomocą wywołania naszej funckji get_queue_ds. Następnie wywołujemy sscanf() aby zmienić mode w strukturze msg_perm. Zmian dokonuje wywołanie msgctl() z poleceniem IPC_SET.

BĄDŻ OSTROŻNY! Istnieje możliwość zablokowania siebie zmieniając prawa dostępu dla kolejki! Pamiętaj, że takie obiekty IPC nie znikają do czasu poprawbengo usunięcia lub restartu systemu. Nawet jeżeli nie możesz zobaczyć kolejki za pomocą ipcs nie oznacza to że jej tam nie ma.

Aby to zademonstrować przedstawię pewną zabawną anegdotę. Kiedy uczyłem budowy wewnętrznej Unixa na Uniwersytecie Południowej Florydy wpadłem w pewną zawstydzającą blokadę. Dzień wcześniej przed zajęciami połączyłem się z serwerem laboratoryjnym w celu kompilacji i przetestowania pracy laboratoryjnej na dany tydzień. Podczas testowania zdałem sobie sprawę, iż zrobiłem literówkę w kodzie odpowiedzialnym za zmianę praw dostępu do kolejki. Jednakże gdy próbowałem zmienić prawa dostępu z "660" na "600" straciłem prawa do własnej kolejki. Nie mogłem przetestować programu w tym samym obszarze katalogu domowego, ponieważ użyłem ftok() do utworzenia klucza IPC ( próbowałem użyć kolejki do której nie miałem prawa ). Zakończyło się to na skontaktowaniu się z lokalnym administratorem rano przed zajęciami, tłumaczeniu mu przez godzinę czym jest kolejka wiadomości i dlaczego potrzebuję uruchomić ipcrm. grrrr.

Po pomyślnym odebraniu wiadomości zostaje ona usunięta z kolejki. Jednak, jak wspomniałem wcześniej, obiekty IPC pozostają w systemie do czasu usunięcia lub restartu. Zatem, nasza kolejka istnieje wewnątrz jądra, gotowa do użytku długo po tym jak nasza wiadomość znikneła. Aby dokończyć cyklu życiowego kolejki powinniśmy ją usunąć za pomocą msgctl(), podając jako polecenie IPC_RMID:


   int remove_queue( int qid )
   {
           if( msgctl( qid, IPC_RMID, 0) == -1)
           {
                   return(-1);
           }
           
           return(0);
   }
   

Powyższa funkcja zwraca 0 po usunięciu kolejki, jeżeli się to nie udało zwracane jet -1. Usuwanie kolejki jest z natury atomowe, zatem jakiekolwiek próby dostępu do kolejki zakończoną się marnie.


next up previous contents
Next: msgtool: Interaktywny manipulator kolejką Up: Kolejki wiadomości Previous: WYWOŁANIE SYSTEMOWE: msgsnd()   Contents

2000-03-01


Poltronic