[updated] Jelzéskezelés/szignálkezelés C-ben Linux-on

Fórumok

Update: http://hup.hu/node/106017#comment-1338790

Kedves Fórumtársak!

Egy programot igyekszem írni, aminek lényege az, hogy egy adott kernelben lakó driverhez (ez a rész adott/gyártói hákmány) beregisztrálja (elküldi a folyamat PID-jét és egy timeout értéket) magát (ioctl/karakteres eszközfájl segítségével).
A kernel driver pedig egy-egy szignált (SIGUSR1/SIGUSR2) küld a korábban regiszrált PID-re, ha az adott hardvereszközök inaktivitása túllépi a korábban beállított timeout-ot.
Ekkor a programom lehetőséget ad egy előre beállított program futtatására (nem muszáj használni, de lehetőséget nyújt rá.)
A programom először forkol, majd démonként fut tovább, és a démon jelentkezik be a szignálok kezelésére.
Ezt a lépést a sigwait függvény segítségével próbáltam megoldani, de eddig úgy tűnik, hogy nem működik. A sigwait()-es ciklusig eljut a program, ellenben ha SIGUSR1/SIGUSR2-t küldök neki, akkor rögtön meghal a démon, a szignálkezelő nem fut le, annal ellenére, hogy a példáknak megfelelően írtam meg a kódot:


...
  //register listeners for signals from the kernel  
  
  sigemptyset(&listen_set);
  sigaddset(&listen_set, SIGUSR1);
  sigaddset(&listen_set, SIGUSR2);
  sigaddset(&listen_set, SIGTERM);
  sigaddset(&listen_set, SIGQUIT);
  if (sigprocmask(SIG_UNBLOCK, &listen_set, NULL) != 0) {
    syslog(LOG_ERR, "sigwait() failed: %s\n", strerror(errno));
    cleanup_daemon();
    exit(ERR_SIGPROCMASK_FAIL);
  }
  
  /*
  The SIGUSR1 and SIGUSR2 signals are set aside for you to use any way you want.
  They're useful for interprocess communication. Since these signals are normally fatal,
  you should write a signal handler for them in the program that receives he signal. 
  */
  
  if (pm0_conf.m_verbose == true) {
    syslog(LOG_INFO, "Starting signal processing loop.\n");
  }
  
  while (true) {
      if (sigwait(&listen_set, &sig) == 0) {
          if (pm0_conf.m_verbose == true) {
            syslog(LOG_INFO, "Got a signal: %d.\n", sig);
          }
          switch (sig) {
            case SIGUSR1:
              syslog(LOG_NOTICE, "SATA HDD-1 standby initiated...\n");
              if (pm0_conf.m_suspend_exec != NULL) exec_suspend(1);
              break;
            case SIGUSR2:
              syslog(LOG_NOTICE, "SATA HDD-0 standby initiated...\n");
              if (pm0_conf.m_suspend_exec != NULL) exec_suspend(0);
              break;
            case SIGQUIT:
            case SIGKILL:
            case SIGTERM:
              if (pm0_conf.m_verbose == true) {
                syslog(LOG_INFO, "Caught SIGKILL/SIGTERM/SIGQUIT, now exiting...\n");
              }
              cleanup_daemon();
              exit(ALL_OK);
              break;
            default:
              break;
          }
      }
      else {
          syslog(LOG_ERR, "sigwait() failed: %s\n", strerror(errno));
          cleanup_daemon();
          exit(ERR_SIGWAIT_FAIL);
      }
  }
...

UI: Igen, tudom, hülyeség diszkek elalvásakor programot futtatni, mert akkor a diszk felébred, de ettől függetlenül a programban biztosítani akarom a lehetőséget erre.

Hozzászólások

Igen, rendben. Blokkolni kellene, de már csak a sigwait hívás után. (Azért, hogy a jelzést kezelő kódrészt egy újabb jelzés ne szakíthassa meg. (Ha minden igaz.))
Ettől függetlenül a sigwait() hívás utáni kód a tapasztalataim szerint már nem is kerül végrehajtásra, tehát nem ez a fő probléma itt.
Ettől függetlenül köszönöm az észrevételt. Jogos.
-------------------------------------------------------------------------------
Az életben csak egy dolog a szép, de az épp nem jut eszembe.

Slackware Linux 13.37 | 2.6.39.3-janos

Ok, a blokkolás lényege most már világos. Köszi, sokat segített.
-------------------------------------------------------------------------------
Az életben csak egy dolog a szép, de az épp nem jut eszembe.

Slackware Linux 13.37 | 2.6.39.3-janos

nade hol mondod meg, hogy melyik signalra milyen függvényt hajtson végre?

man sigaction

int sigwait(const sigset_t *set, int *sig);

The sigwait() function suspends execution of the calling thread until the delivery of one of the signals specified in the signal set set. The function accepts the signal (removes it from the pending list of signals), and returns the signal number in sig.
--
Amennyire én értem, ilyenkor nem kell sigaction-nal regisztrálni a jelzést kezelő függvényt, mert a sigwait feleslegessé teszi. Addig blokkolja a hívó folyamatot, amíg nem érkezik egy - a paraméterben átadott jelzéscsoportba tartozó - jelzés.
-------------------------------------------------------------------------------
Az életben csak egy dolog a szép, de az épp nem jut eszembe.

Slackware Linux 13.37 | 2.6.39.3-janos

Valahogy így:


  sigset_t w;
  sigemptyset( &w );
  sigaddset( &w, SIGALRM );
  sigprocmask( SIG_BLOCK, &w, NULL );

  alarm( 5 );
  puts( "alarm(5)" );

  int s;
  if( !sigwait( &w, &s ) )
    printf( "signal: %s (%d)\n", strsignal(s), s );
  else
    printf( "error: %d\n", errno );

Egy érdekességre lettem figyelmes a programomban.

Konkrétan arról van szó, hogy van egy függvény ami meghív egy előre beállított programot (exec_suspend), a megadott paraméterekkel.
Mielőtt azonban ész nélkül nekiesne, megnézi, hogy végrehajtható-e az adott parancs (check_exec), a programot futtató felhasználó számára.

A probléma abban áll, hogy a check_exec függvénynek egy stat struktúrára mutató pointert adok át, amit az exec_suspend függvényben töltök fel, és a stack-en kap helyet (nem dinamikusan foglalt memóriában.) A stat struktúrából akarom kiolvasni, hogy a programot futtató felhasználónak van-e futtatási joga.
A check_exec függvényen belül viszont már valamiért "elromlik" a stat strukturában tárolt információ.
A program kimenetén ez látszik:


Aug 27 23:12:30 ganymede pm0[2612]: SATA HDD-0 standby initiated... 
Aug 27 23:12:30 ganymede pm0[2612]: /bin/bash is owned by UID 0 and GID 0. 
Aug 27 23:12:30 ganymede pm0[2612]: pm0 running with UID 0 (root) and GID 0 (root). 
Aug 27 23:12:30 ganymede pm0[2612]: The executable is owned by UID 473668 ((null)) and GID 616544 ((null)).

Nem tudom mi lehet a gond...
Lehet, hogy a függvényhívás során felülíródik a stack-en tárolt struktúra?
Másra nem tudok gondolni, mert a struktúrát nem módosítom.

Itt a releváns kódrészlet:


bool check_exec(struct stat const *filestat) {
	uid_t u = getuid();
	gid_t g = getgid();
	
	if (pm0_conf.m_verbose == true) {
		struct group *grp = NULL;
		struct passwd *pwd = NULL;
		char *usr_str, *grp_str, *default_val = "[unknown]";
		
		if ((pwd = getpwuid(u)) == NULL) usr_str = default_val;
		else usr_str = pwd->pw_name;
		if ((grp = getgrgid(g)) == NULL) grp_str = default_val;
		else grp_str = grp->gr_name;
		
		syslog(LOG_INFO, "%s running with UID %d (%s) and GID %d (%s).\n", EXEC_NAME, u, usr_str, g, grp_str);
		
		if ((pwd = getpwuid((*filestat).st_uid)) == NULL) usr_str = default_val;
		else usr_str = pwd->pw_name;
		if ((grp = getgrgid((*filestat).st_gid)) == NULL) grp_str = default_val;
		else grp_str = grp->gr_name;
		
		syslog(LOG_INFO, "The executable is owned by UID %d (%s) and GID %d (%s).\n", EXEC_NAME, filestat->st_uid, usr_str, filestat->st_gid, grp_str);		
	}
	
	if (u == filestat->st_uid) {
		if ((filestat->st_mode & S_IXUSR) == S_IXUSR) return true;
	}
	else {
		if (g == filestat->st_gid) {
			if ((filestat->st_mode & S_IXGRP) == S_IXGRP) return true;
		}
		else {
			if ((filestat->st_mode & S_IXOTH) == S_IXOTH) return true;
		}
	}
	
	return false;
}

void exec_suspend(int n) {
	struct stat stat_buf;
	pid_t cp;
	int i;
	
	char *string_buf, *string_buf_end;
	int sring_len;
	
	if (stat(pm0_conf.m_suspend_exec, &stat_buf) == -1) {
		syslog(LOG_ERR, "stat() failed on \'%s\': %s\n", pm0_conf.m_suspend_exec, strerror(errno));
		return;
	}
	else {
		if ((stat_buf.st_mode & S_IFMT) != S_IFREG) {
			syslog(LOG_ERR, "\'%s\' is not a regular file!\n", pm0_conf.m_suspend_exec);
			return;
		}
		
		if (pm0_conf.m_verbose == true) {		
			syslog(LOG_INFO, "%s is owned by UID %d and GID %d.\n", pm0_conf.m_suspend_exec, stat_buf.st_uid, stat_buf.st_gid);		
		}
		
		if (check_exec(&stat_buf) != true) {
			syslog(LOG_ERR, "\'%s\' is not a executable for the user/group on whose behalf %s is running on!\n", pm0_conf.m_suspend_exec, EXEC_NAME);
			return;
		}
	}

-------------------------------------------------------------------------------
Az életben csak egy dolog a szép, de az épp nem jut eszembe.

Slackware Linux 13.37 | 2.6.39.3-janos

Jaj, köszi!
Több szem többet lát. :)
Nem is gondoltam, hogy minden OK, csak eggyel több paramétert kap a kiíratás, és ezért borul látszólag minden.
A copy-paste átka...
-------------------------------------------------------------------------------
Az életben csak egy dolog a szép, de az épp nem jut eszembe.

Slackware Linux 13.37 | 2.6.39.3-janos