LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Programming (https://www.linuxquestions.org/questions/programming-9/)
-   -   writtign my own linux shell using BASH, guidelines? examples? thx (https://www.linuxquestions.org/questions/programming-9/writtign-my-own-linux-shell-using-bash-guidelines-examples-thx-637398/)

cocchiararo 04-23-2008 07:05 PM

writtign my own linux shell using BASH, guidelines? examples? thx
 
Hello, i've spent many days already searching and searching on the topic, and i couldnt find enough info to get me (and my team) started. We have (for our college) to write a shell that will be mounted (or whatever the correct translation for "montar" applies here) on bash (Debian etch). Its a basic shell (or so we have been told), but its something "extra", not explained on the clases or anything, so we are on our own here. (we only got a brieff explanation with examples on bash scripting, but google was more usefull on that matter than the class :P, but i wasnt able to find example shells or something that pointed us in the right direction, only a LOT of general info about shell scripting), but we know the basics of programing from other assingments/classes we had in the past.

What we must do:
-write a core for the shell, that will:
1) check if there are no modules loaded for the user that logs in
2) periodically check if the config files for the logged user has changed
3) some more stuff
4) always wait for input commands

* the modules are what will do stuff with the comands, there are some, the security one is for checking if the command entered is available to the user or not, for example

Now, the thing is, that we have not been able to find a simple (or complex, or whatever) shell, written in bash, to see how it works...

what we think for now, is that the main script will have a loop (while), and will keep waiting for commands to be entered... now.. how do we make it recieve the commands, and send em to the modules (if there is any) or to bash (if the command passed all the modules without errors, or if there are no modules loaded), or execute it by itself if it was a built in...


all this, while from time to time (set intervals), check for configuration changes :P

NOW, we still have many doubts about how to manage the commands entered... some options are:

read $variable, and work with the variable, but some commands (cd for example) seems not to work properly (we are still experimenting).

cat "something" (its not my idea and i didnt understand it well yet)

other ?

this is my first post in a programming forum, so i might not be doing it ok, so, my appologies, and any help will be appreciated.

jschiwal 04-23-2008 07:27 PM

I'm not sure if what you need to do is modify bash, such as loadable built-ins or if you are supposed to create your own shell.

For the former, read the "Learning the Bash Shell" book and/or "Bash Cookbook". Also
~/examples/loadables/* in any bash taqrball newer than 2.0. For the latter, maybe something like "Advanced Unix Programming" would be helpful.

But saying you want to write your own shell using bash doesn't make much sense. When the kernel loads in a program, it looks for the magic bytes to determine which shell should be used. If you use bash, then bash is the shell.

wrhansen 04-23-2008 07:38 PM

I had to write a shell in a class about a year ago. I wrote it in C though.
Our shell was a little different though, all it had to do was wait for the user to enter a command, and upon the user hitting "Enter" the shell would look at a file that contained a list of commands that the user was allowed to use, and if their command was in the list, the shell would fork a process and execute the command.

The very backbones of a shell is simple:

Code:

while( not some user inputted stop )
{
    Read User input
    If User input is a command
        Execute it
    Else
        Spit out an error
}

Edit-The above code is a really sloppy pseudo code.
Let me know if that helps, I can give some actual code samples( in C ) if you need. But I'll have to dig for them.


wrhansen

jschiwal 04-23-2008 07:58 PM

As this is a homework problem, we can't simply do the work for you. It is against the policy of this site. Also, you wouldn't learn what you need to. If you have something you are trying and need with some code you are writting, then we can help with that.

cocchiararo 04-23-2008 09:22 PM

i dont want to have my homework odne, thats why i explained very little about what i need to do :P

As i said, i must write a shell using bash scripts, the shell must loaded when a user who has it configured for himself, logs in.

But i dont want info on that, as i said, we know our way arround programming (more or less :P), but we have many doubts about "the beguining" (in this case, after the script is automatically loaded, inside the "while", we are unsure what method to use to "catch" the commands, and then send them to whatever applies.

we started with the most basic case, wich would be NO modules loaded, so the command gets sent to bash directly for procesing, and already run into trouble with some commands not working when we "stored" the command on a variable (using read).

as i said, it must be written using shell scripts, in bash.

any help thats admited by the site policy will be appreciated (an example, pointing in the right direction, some usefull/related section in guide, etc).

our first problem, as i said, is the method for taking the input and procesing it/deciding what to do with it.

pd: as for the pseudo code, its more or less similar to what we thought, we have the idea, but not the "knowledge/method" to code it :P (at least not the part we are having trouble with )

EDIT: if i undestood correctly, i cant get the beforementioned help, cause it is against the site policy ? if so, ill get what we have coded so far, post it here, and ask about what's not working, but not unltil tomorow or friday, we have a group meeting otmorow i think.

jschiwal 04-24-2008 02:55 AM

You have access in bash to argc & *argv[]. Also look at the "getopts" builtin as well as as the eval command.

cocchiararo 04-24-2008 07:18 AM

well, to hell with the weather, today i woke up to find the WORST fog in ages, imposible to leave home (college its like 70 km away).

our meeting is canceled, so i began the "experiments"

my partner wanted to use read to store the command in a variable, and then he was using it by doing the following:

echo $variable | bash

cd doesnt wokr with that (other commands do), but i dont know why he wants to do than instead of just:

$variable

here is a summary of the "homework":

The proyect is to develop a shell (from now on myshell) built only by shell scripting. Such script will be executed by (or over, dunno how to correctly translate) bash, wich is the default shell asociated to every user in linux.
Myshell primary caracteristics will be the posibility of adding/removing modules in a dinamic way, establish configurations for each user, and, by being "mounted over bash", use all the functionality that it offers. In this way, we can think of myshell as a wrapper of bash, to wich it adds functionalities.

maybe with that rough translation of the "introduction/summary"of the proyect, i make things clearer.

pd: ill look into what you mentioned, i havent heard of "getopts" before, not sure about eval, argc and *argv[] im familiar with in c, will look into it too, but if its similar to C in bash, i dont see how it can help with "our starting problems" :P

indienick 04-24-2008 08:13 AM

Essentially, I see cocchiararo's task as writing a Bash superset shell - on top of the very thing it's superseding (Bash).

Reading the project's summary, I would like to point out that a "wrapper" is something that extends functionality to another entity - GTK+ is in C, gnome-java is GTK+ for Java which is wrapped using the Java Native Interface.

The basic methodology here is:
  1. Write the basic shell routine, as wrhansen described.
  2. In each user's ~/.bashrc file, or in the system-wide /etc/profile, add a line to execute your shell every time a new shell is started.
  3. Make sure that when the user types "exit" to kill a shell session, it also kills the Bash session that is hosting your "myshell" session - this can be tricky.

cocchiararo 04-24-2008 09:46 AM

Quote:

Originally Posted by indienick (Post 3131415)
Essentially, I see cocchiararo's task as writing a Bash superset shell - on top of the very thing it's superseding (Bash).

Reading the project's summary, I would like to point out that a "wrapper" is something that extends functionality to another entity - GTK+ is in C, gnome-java is GTK+ for Java which is wrapped using the Java Native Interface.

The basic methodology here is:
  1. Write the basic shell routine, as wrhansen described.
  2. In each user's ~/.bashrc file, or in the system-wide /etc/profile, add a line to execute your shell every time a new shell is started.
  3. Make sure that when the user types "exit" to kill a shell session, it also kills the Bash session that is hosting your "myshell" session - this can be tricky.

most of the summary is translated by me, exept wrapper, that was in english, with its meaning translated to spanish between brackets.

i dint explain more, but configuring "myshell" to each user will be done with makefile, wich must have an option for install/configure/uninstall, and will be manually used by root/the administrator when they test our "myshell". (so we dont need to manually add anything, we must prepeare a makefile that does it)

for now we are trying to learn how to start writing our bash superset shell (thx naming our task XD), wich we are having trouble finding info/examples. (as i said, the basic idea is there, not how to correctly code it using a bash script :P )

the basic idea for the loop would be:

Code:

while( comand != exit)
{
    Read User input
    send it to loaded modules, one at a time
    if none returns an error msg
      execute it/send it to bash for execution (whichever aplies)
    Else
        Spit out an error
}

thats mostly pseudo code or worse :P
now, the thing is, if i write a script, lets say... like this (here there is no sending the comand to modules, its just executed, i thought that woul be an inteligent way to start :P):

Code:

#!/bin/bash
while [ COMANDO != exit ]
do
read COMANDO
$COMANDO
done

i get no promt between comands, nor can i "browse" to a previous command i may have used by pressing the up/down arrows.

ill KEEP investigating, but i already saw (forgot the past tense of read) many bash tutorials, and none informed me about how to start with this :P

indienick 04-24-2008 02:38 PM

I'm glad I could give your project a name! (jajajajaja) For getting some of that functionality, try searching for things like interpreting certain keystrokes within the shell; every keystroke sends a signal over its connector pins, it's just a matter of saying "when [this] keystroke is noticed, do [that]".

FYI: The past tense of "read" is, oddly enough, "read". It's just pronounced differently. In the present tense, it's pronounced as "reed", and in the past tense, "red".

ntubski 04-24-2008 06:11 PM

Quote:

i get no promt between comands, nor can i "browse" to a previous command i may have used by pressing the up/down arrows.
The read and history commands have some options that help with this:
Code:

$ help read
read: read [-ers] [-u fd] [-t timeout] [-p prompt] [-a array] [-n nchars] [-d delim] [name ...]
    ... If the -p option is supplied, the string PROMPT is output without a trailing newline
    before attempting to read... If -e is supplied and
    the shell is interactive, readline is used to obtain the line...

$ help history
history: history [-c] [n] or history -awrn [filename] or history -ps arg [arg...]
    ...
    If the -s option is supplied, the non-option ARGs are appended to
    the history list as a single entry....

Quote:

i dont know why he wants to do than instead of just:

$variable
Try running echo foo | cat with this method and you'll see it doesn't give quite the same as the shell. This is where you should use eval:
Code:

$ help eval
eval: eval [arg ...]
    Read ARGs as input to the shell and execute the resulting command(s).


cocchiararo 04-24-2008 07:00 PM

Quote:

Originally Posted by ntubski (Post 3131974)
The read and history commands have some options that help with this:
Code:

$ help read
read: read [-ers] [-u fd] [-t timeout] [-p prompt] [-a array] [-n nchars] [-d delim] [name ...]
    ... If the -p option is supplied, the string PROMPT is output without a trailing newline
    before attempting to read... If -e is supplied and
    the shell is interactive, readline is used to obtain the line...

$ help history
history: history [-c] [n] or history -awrn [filename] or history -ps arg [arg...]
    ...
    If the -s option is supplied, the non-option ARGs are appended to
    the history list as a single entry....



Try running echo foo | cat with this method and you'll see it doesn't give quite the same as the shell. This is where you should use eval:
Code:

$ help eval
eval: eval [arg ...]
    Read ARGs as input to the shell and execute the resulting command(s).


we had already found (well, not me, my parter thats has more experience :P ) about the -p for read (and he was really happy when i showed him this post about the -e part :P), but we are were unable for now to produce the decired prompt with it (an echo of $PS1 gives ${debian_chroot:+($debian_chroot)}\u@\h:\w\$, and an echo of that gives u@h:w$), but its the least of our problems for now).

about why he did echo "comand" | bash, after some testing i noticed that commands with pipes wouldnt work my way :(, but a cd command wont work his way either, so now we are looking into what you said.

thx for the help/info

EDIT: oh, just noticed you were telling me to use eval $comando, i kept reading and reading history and eval help until my teammate decided to try it instead :P

cocchiararo 04-30-2008 09:55 PM

Hello again !!

we've been doing a lot of work + reading

we are like 2/3 done i think, but we have a few things we are having trouble with.

for now ill bother you gentlemen with this:

one of the modules (the last one in fact, tho we skiped 2 to go to this one that seemed harder), needs to "establish a limitation on the ammount of outgoing IP packets (or paquets or packages, dunno how to translate :P)"

When the module detects (the module is run every X minutes,X is configurable, and only when run, it will do anything), that the amount of outgoing IP paquets (or the other options i mentioned earlier :P) is higher than the set number (we set this number), for each active process started in the current session that has 1 or more sockets with external conections, we must print on screen the sockets, and then kill the process.

I have no idea how to do this for now (im doing a different part), but my teammate spent the whole day researching, finally thought that he was going to get the desired info with "netstat --up -p", but when he runs that comand, besides some usefull info, he gets a msg saying:

(Not all processes could be identified, non-owned process info
will not be shown, you would have to be root to see it all.)

and since he is using the command in a while loop, the message apears a few times.

he was really angry and went to bed :P any help/hint/push in the right direction will make him happier xD

chrism01 05-01-2008 12:25 AM

From the tcpdump man page
" When tcpdump finishes capturing packets, it will report counts of:

packets ``captured'' (this is the number of packets that tcpdump
has received and processed);

"

ntubski 05-01-2008 12:21 PM

Quote:

Originally Posted by cocchiararo (Post 3138350)
I have no idea how to do this for now (im doing a different part), but my teammate spent the whole day researching, finally thought that he was going to get the desired info with "netstat --up -p", but when he runs that comand, besides some usefull info, he gets a msg saying:

(Not all processes could be identified, non-owned process info
will not be shown, you would have to be root to see it all.)

and since he is using the command in a while loop, the message apears a few times.

he was really angry and went to bed :P any help/hint/push in the right direction will make him happier xD

To hide the error message you could do "netstat --up -p 2>/dev/null", assuming the error message goes to standard error, and the useful info is on standard out. Otherwise you could do some filtering with grep/sed/awk/...

cocchiararo 05-01-2008 05:48 PM

well, we dont have the tcpdump command (does it come with debian etch ?, if it doesnt, we cant use it for this)

hiding the error worked tho xD

chrism01 05-01-2008 07:08 PM

To be more explicit
/usr/sbin/tcpdump

cocchiararo 05-01-2008 07:30 PM

well, its not there :P

chrism01 05-01-2008 08:48 PM

Here ya go: http://packages.debian.org/tcpdump

cocchiararo 05-01-2008 08:52 PM

i have to ask if we are allowed to install packages that were not provided (thats why i kept saying that i didnt have it, instead of trying to get it :P )

if we are allowed to download packages and installe them (when we execute "make instalar"), ill check it.

cocchiararo 05-01-2008 10:10 PM

ok, my teammate gave me this:
#!/bin/bash
Code:

case $1 in
procesar) Cant_Paq=`netstat -s | awk '{if (NR == 6){print $1}}'`
          if [ $Cant_Paq -gt $MAX_PAQ ]; then
          netstat --ip -np 2> /dev/null > network
          ps -o pid= | while read PID
          do
          Proc=`cat network | grep $PID`
          if [ "$Proc" != "" ]; then
          echo "$Proc"
          kill $PID
          fi
          done;;
informacion) echo "El maximo numero de paquetes permitido es $MAX_PAQ"
            echo "Numero de paquetes salientes:" `netstat -s | awk '{if (NR == 7){print $1}}'`;;
iniciar) export MAX_PAQ=`cat /etc/michelle/modulos/red/config/$USER`;;
detener) export -n MAX_PAQ;;
esac
exit 0

he was really happy, until he understood (or so he thinks) that the info that he gets from netstat (about outgoing packets) is always "growing", each time theres an outgoing packet, the number netstat -s gives, grows by 1, and it wont go down when a proces that was osing a socket is killed.

so we once again find ourselves without knowing how to work with sockets and this problem, and still have no answer from the university if we can download and install (and use) tcpdump yet.

cocchiararo 05-03-2008 04:05 PM

ok, we are "almost" done, we have finished everything but:

net limiter module
socket limitation in the general limitations module
ssh-kegen (or something like that) for automatick log in into a remote computer for saving a log
installer (make)

netstat was not the tool to use for the packets thing in the net limiting module, we still have no answer from the guy who has to tell us if we can or can not install tcpdump, we turned our attention to /proc/net/dev

that file has the ammount of packets sent, but its a cumulative ammount, each time a packet is sent, its added there. Initially we thought that we had to check the current sent packets, and if in the moment we checked, they were higher thanwhat was allowed, we had to kill proceses using sockets for outgoing conections. (thus, stoping outgoing packets). If anyone knows of something (diferent from tcpdump :P) that can check the number of outgoing packets in a given instant (not the total since the user logged in), pls inform us, for now, we'll asume that the module will check outgoing packets (total), and from the first time it detects that the limit has been exeded, each time its executed, it will kill proceses that are sending packets. (its not ilogical, but we arent 100% sure this is what the module must do).

with that done, the only thing we need info about, is: "how to check the actual amount of sockets open by active proceses". In the meantime, will look into /proc too for that info (but we still dont know the actual file that may have the info).

cocchiararo 05-03-2008 09:35 PM

ok, weare done (considering that our understanding of the net limiter module is correct :P )

we have 2 small problems we would like your help to solve (besides making the makefile, but we dont want help for that... yet :P )

a) shutdown: it needs us to be root... any way to solve this little problem ? (especially one that wont require root pasword :P)
we will install with make instalar, and then configure it with make configurar (wich will ask for a username) as root tho, so any modifications that need us to be root, can be done in make instalar /make configurar, but we are unsure what to modify :P

EDIT: we are looking into the posibility of creating a group that will be allowed to use shutdown, and then add the user we configure michelle as the shell to, but that would have to be done doring make instalar (creating the group) and make configurar (putting the user in the group)

b) one module logs EACH command in a local file, but once it has a certain size, we need to start loguin it in a remote log (the ip of the remote pc and the size of the file are set in a config file). We understand that we can do this with ssh, more specifically, with this command:
echo "$2" | ssh $IPSERVERLOG 'cat /home/$USER/logauditoria'

$IPSERVERLOG is the value that was previously taken from the config file, and its an IP, $2 is the command that will be logged.

but in order for that to work without asking for paswords, we must put the file with the key (this might sound ignorant, but thats how it was explained to me by my teammate :P) in the remote pc, and we'll need a pasword for that too :P
As far as i know its ILOGIC to think that we will be able to write to another pc without ever knowing the pasword, so we might be allowed to put the necesary files on the remote pc before testing the proyect (i hope), but more info might be appreciated :P

ntubski 05-04-2008 05:11 PM

Quote:

Originally Posted by cocchiararo (Post 3141705)
ok, weare done (considering that our understanding of the net limiter module is correct :P )

we have 2 small problems we would like your help to solve (besides making the makefile, but we dont want help for that... yet :P )

a) shutdown: it needs us to be root... any way to solve this little problem ? (especially one that wont require root pasword :P)
we will install with make instalar, and then configure it with make configurar (wich will ask for a username) as root tho, so any modifications that need us to be root, can be done in make instalar /make configurar, but we are unsure what to modify :P

See http://en.wikipedia.org/wiki/Setuid

Quote:

b) one module logs EACH command in a local file, but once it has a certain size, we need to start loguin it in a remote log (the ip of the remote pc and the size of the file are set in a config file). We understand that we can do this with ssh, more specifically, with this command:
echo "$2" | ssh $IPSERVERLOG 'cat /home/$USER/logauditoria'
I think you mean
echo "$2" | ssh $IPSERVERLOG 'cat - >> /home/$USER/logauditoria'

Quote:

but in order for that to work without asking for paswords, we must put the file with the key (this might sound ignorant, but thats how it was explained to me by my teammate :P) in the remote pc, and we'll need a pasword for that too :P
As far as i know its ILOGIC to think that we will be able to write to another pc without ever knowing the pasword, so we might be allowed to put the necesary files on the remote pc before testing the proyect (i hope), but more info might be appreciated :P
see Public_key_authentication_with_ssh

cocchiararo 05-04-2008 05:33 PM

Quote:

Originally Posted by ntubski (Post 3142633)
See http://en.wikipedia.org/wiki/Setuid


I think you mean
echo "$2" | ssh $IPSERVERLOG 'cat - >> /home/$USER/logauditoria'



see Public_key_authentication_with_ssh

we created a group for the shutdown thing, but ill look into setuid too.

i dont know about the command for remote loguin with ssh, since my teammate had 2 virtual machines and used that to test, but ill ask him.
(and forward him the link :P )

cocchiararo 05-04-2008 10:37 PM

ok... we have a little problem :P

in make configurar (configurar = configure, and its one of make's targets), we must ask the user (root) to input the user to which he want myshell to be configured.

we thought of doingh this:

Code:

        read -p "write username to configure the shell to: " Usuario
        chsh -s /usr/bin/michelle.sh ${Usuario}

when it DIDNT work, i did some checking, and i noticed that Usuario was blank.
then some reading revealed that ${Usuario} was a make variable (if i set it to something in the variables section, Usuario keeps that value), so $$Usuario or $${Usuario} should have worked, or so i thoght, buy no :(
do i need to change something in the read command ? or in the way i want to expand the variable ? :P
If everything fails, i could call for a secondary script for doing the read and chsh stuff, but i don't know if it will be acceptable :P (we arent compiling anything, but they said the wanted make to be the install/configure/uninstall tool)

ntubski 05-05-2008 01:50 PM

The problem is that each line is executed in a new subshell, so the second line doesn't see the variables set by the first one. You could try
Code:

        read -p "write username to configure the shell to: " Usuario ; \
        chsh -s /usr/bin/michelle.sh $${Usuario}

The backslash makes it all one line, then you need the semicolon as a command seperator

cocchiararo 05-05-2008 04:05 PM

i tried that (well, i actually was forced too, since the code i pasted before is inside an "if", but it wont work.

dunno, might try that, but i too read that every line is executed in a separated /bin/sh shell, so :p

for now we'll manage with calling a script in the make configurar section.

now, i want to present you with a "bigger" problem:

Our main script calls many subscripts, some run in background (script.sh & or . script.sh &, depending what we need).
Now, the first thing that is run, is a script that "register's" each module set for the user.
we were really happy yesterday when we finally installed our shell, and the security module worked... but then, for diferent reasons, we ended up noticing that, the "check modules" script, wich is run with & (background) wouldntexecute the script that "registered modules" when it should. Upon further investigation, we notices that the "register modules"script, DOES NOTHING if executed with & (and thus, we conclude that the "check modules" script doesnt work cause its run in background, and calls "register modules" when needed).

supose theres a father/main script that uses 2 arrays.
it then calls for:
. registrar.sh

(. is used cause we first used export on the 2 arrays, but we cant export arrays, or so we think, and also, exporting makes the array visible for the child script, but changes made to it wont be seen by the parent)

that works well.

now, if we change that to:

. registrar.sh &

it either does nothing, or since it runs in a diferent shell/background, our main script never gets the changes on the arrays.

any way arround this ? we are more or less screwed if there is none :P

EDIT: we are running it in that what, cause there are 2 scripts that have to run every x minutes, and they wont run if we put them insile the read-eval loop, if the user leaves the computer and writes nothing. We first thought of using read -t "timeout time", but we dont like the fact that it might be posible for the user to take a LONG time to write a long command, and the timeout would execute it when he was halfway writing it...
but it might be our only chance (with read t we can put the periodic scripts back into the loop, and make a check in them if they will do ther "thing" or just end)

chrism01 05-05-2008 06:04 PM

Use the sudo tool for safe shutdown, better than setuid. Would only require the user's passwd (not root's) to activate.

for timeout, make it longer eg 5 mins.

Separate files eg registrar.sh, whether run in the background or called directly inherit any shell vars that have been explicitly exported, but it's a one-way process ie you can't make changes to a var in a sub-shell prog and expect them to propagate up to the parent... it's downwards only.
You can use various other methods eg a temp file or a rtn code.

don't know if you've seen these pages, but they are very useful:

http://www.tldp.org/LDP/abs/html/
http://rute.2038bug.com/index.html.gz

ntubski 05-05-2008 08:14 PM

Quote:

Originally Posted by cocchiararo (Post 3143822)
i tried that (well, i actually was forced too, since the code i pasted before is inside an "if", but it wont work.

hmm, works for me...

Quote:

now, i want to present you with a "bigger" problem:
...
EDIT: we are running it in that what, cause there are 2 scripts that have to run every x minutes, and they wont run if we put them insile the read-eval loop, if the user leaves the computer and writes nothing. We first thought of using read -t "timeout time", but we dont like the fact that it might be posible for the user to take a LONG time to write a long command, and the timeout would execute it when he was halfway writing it...
but it might be our only chance (with read t we can put the periodic scripts back into the loop, and make a check in them if they will do ther "thing" or just end)
I think you could use traps and signals, the trap handler executes within the current shell, so you could source the script from there. Then you just need another script sending a signal every x minutes.

cocchiararo 05-05-2008 11:11 PM

we ended up using read -t, but ill inform my group about the other sugestions that may be usefull if our actual choise has problems.

pd: will try your exact code to see if i did anything wrong last time :P

EDIT: you where right, it works, dunno why it didnt work last time :P

cocchiararo 05-16-2008 01:35 PM

big trouble !

we thought we were done, but when we tested the periodic functions (setting them to run each second), we discovered that the signals/traps we were using cause a segmentation fault and everything falls appart :(

here is an example that causes the crash:
Code:

#!/bin/bash
declare -r tmpoV=1 #this sets how often verificar.sh has to run
declare -r tmpoA=1 #this sets how often admin_modulos_per.sh has to run
FactCfgs=`ls -tcRn --full-time /etc/michelle | grep -w "$USER"$'\n'"periodicos.config"$'\n'"comando.config" -m 1 | awk '{print $6$7}'` #this is used to collect the actual modification date of a bunch of files, and store the newest

Tver () {
. /usr/bin/verificar.sh
}
Tadm_per () {
. /usr/bin/Desktop/admin_modulos_per.sh
}

/usr/bin/control_tmpo.sh $$ $tmpoV "USR1" &
/usr/bin/control_tmpo.sh $$ $tmpoA "USR2" &

trap Tver "USR1"
trap Tadm_per "USR2"

while true
do
read Com
done

control_tmpo looks like this:
Code:

#!/bin/bash

while true
do
sleep $2
kill -s $3 $1
done

verificar:
Code:

#!/bin/bash
FmodCfgs=`ls -tcRn --full-time /etc/michelle | grep -w "$USER"$'\n'"periodicos.config"$'\n'"comando.config" -m 1 | awk '{print $6$7}'`
if [ "$FactCfgs" != "$FmodCfgs" ]
FactCfgs=$FmodCfgs
echo -e -n "\nSthere is a change in the config files, executing regiter and initialize modules\n"
. /usr/bin/reg_e_ini_modulos.sh
fi

admin_modules_per
Code:

#!/bin/bash

for x in `seq 1 ${#ModuloP[*]}`
do
        ${ModuloP[$x-1]} Procesar
        if [ $? = 1 ]
        then
                echo "A periodic module failed"
                echo "A periodic module failed" >> /home/$USER/periodicos.log
                exit 1
        fi
done

If we set 1 of the periodic functions not to run, nothings bad happens. If we set both with the same time, it crashes really fast. If we use diferent times, eventually, they try to run at the same time, and for some reason verificar.sh runs while admin_modulos_per.sh is runing (the for cicle was not done), and administrar never ends... then the next time administrar wants to run... segmentation fault.
My partner discovered that if we just put 2 similar senteces inside admin_modulos_per and verificar, it crashes too, he thinks its related to using `` in assignations.
Code:

FmodCfgs=`ls -tcRn --full-time /etc/michelle | grep -w "$USER"$'\n'"periodicos.config"$'\n'"comando.config" -m 1 | awk '{print $6$7}'`
and we put something similar (or identical) in the other script, and everything still falls appart.. but if we dont use that or the `seq 1 ${#ModuloP[*]}`, it doesnt crash :/

are we doing something weird/wron with the signals ? we read that signals/traps dont interrupt each other, if one is running, the other just waits.

a capture of the "error""
Code:

[michelle]zero@debian:~$ /usr/bin/verificar.sh: line 2: wait_for: No record of process 841
Violación de segmento

Violacion de segmento = Segmentation fault

EDIT: also, we found that if we put sleep 1, or just a=a or whatever != than read inside the while cicle, there seems to be no problem :/ (but we need the read in the first place, its a read-eval loop :P )

ntubski 05-16-2008 05:16 PM

I guess the code implementing the `` operator is not reentrant (i.e. doesn't like getting interrupted).

For this particular case you can rewrite
Code:

for x in `seq 1 ${#ModuloP[*]}`
as
Code:

for ((x=1; x <= ${#ModuloP[*]}; x++)) ; do
But that's not a very satisfying solution. You could try implementing mutual exclusion in bash, or maybe avoid using traps after all.

cocchiararo 05-16-2008 05:47 PM

we tried mutual exclusion (we just had the class about IPC, sincronization, etc :P), but no go.

then my teammate changed the control_tmpo.sh and verificar.sh way of working, now there are no sleeps, and everything is fine... but we always use a little cpu :P


All times are GMT -5. The time now is 03:20 PM.