LJDT: Taking Advantage of Screen


Last week I was asked if there was a way to start an application at the command line and later come back to it from somewhere else. Normally in Linux/Unix (*nix) it is possible to ‘background’ a process and then return to it later on but that’s only if you are still in the same session where the process was sent to the background. This is useful to have something run while you do other things but reconnecting to this session cannot be done with just the shell. Thankfully ‘screen’ is on Linux systems by default (all of them I’ve used anyway) and as a result, Linux Just Does That.

So let’s go into the basics of the ‘screen’ command. I’ll be using my SLED 11 laptop and SLES 10 servers for demonstration though this applies equally to OpenSUSE and probably every other current Linux distribution you can find. As mentioned there is sometimes a need to run a command and come back later to check on it, and not always after you have been able to stay in front of the box or consistently connected for hours or days on end. There may be a multi-gigabyte download you are trying to pull down, or a file you are tailing to see what is happening over time, or just some process you run over and over and need to see how that continues without running it manually again. Another use case that is slightly different is one where information needs to be shared between or among individuals who are not necessarily near eachother or wanting to get near eachother to see something happen on a machine. The screen application lets you have as many people as you would like viewing the same terminal, all able to provide their own individual contributions at the same time. Another use case is when you don’t want to remotely access a machine multiple times but instead you just want to have multiple shells and you do not have a GUI in which you can spawn a dozen xterm windows. A final scenario may be the simple desire to not have programs stop when a network connection random dies. I’m sure there are others but these are the ones I use most-often. I’ll elaborate a bit more on each of them here shortly.

Going back to the original introduction there is the concept of the ‘background’ and ‘foreground’ for processes. It is possible, for example, to start a download with wget or curl (two commands used for such things) and then background it with Ctrl+Z followed by ‘bg’ and the number of the job. This looks like the following:

ab@mybox0:/tmp> wget ftp://ftp.novell.com/outgoing/imanager273idm361.zip
--23:12:34--  ftp://ftp.novell.com/outgoing/imanager273idm361.zip
           => `imanager273idm361.zip'
Resolving ftp.novell.com...
Connecting to ftp.novell.com||:21... connected.
Logging in as anonymous ... Logged in!
==> SYST ... done.    ==> PWD ... done.
==> TYPE I ... done.  ==> CWD /outgoing ... done.
==> PASV ... done.    ==> RETR imanager273idm361.zip ... done.
Length: 149,585,409 (143M) (unauthoritative)

 2% [===>                                                                                                                                                            ] 4,304,904     10.26M/s
[1]+  Stopped                 wget ftp://ftp.novell.com/outgoing/imanager273idm361.zip
2009-06-18 23:12:35 Jobs:1 Err:148
ab@mybox0:/tmp> bg 1
[1]+ wget ftp://ftp.novell.com/outgoing/imanager273idm361.zip &

As you can see (by the results I pressed Ctrl+Z after about two percent of the download was completed. My prompt (customized) shows me the number of jobs I have running and it shows I have one. When I paused the job it also gave me a job number in brackets ‘[1]’ so I knew which job to background with the ‘bg’ command right after that. The last line shows that I had backgrounded the job. This is fine and I can then foreground it again with ‘fg 1’ whenever I want to do so but only if I am still in the same session that did the backgrounding. This is covered online as to why that is (even when I’m the same user, and even when I’m ‘root’) and it about as far as I would like to get into it. Suffice it to say it is not currently possible (from everything I’ve found) to restore a process to a shell unless it came from that shell.

This is where one of the use cases comes in. Using screen I can start the process and then detach from the screen and go along my merry way. The benefit here is that I can reconnect to the screen (it’s an application made to do that after all) and see how my progress is after minutes, hours, days, or however long it takes for me to be satisfied enough with the results to do something else. This looks slightly different.

ab@mybox0:/tmp> screen -q      #The -q skips a screen of information that isn't that useful for demonstration purposes

2009-06-18 23:18:02 Jobs:0 Err:0
ab@mybox0:/tmp> wget ftp://ftp.novell.com/outgoing/imanager273idm361.zip
--23:18:05--  ftp://ftp.novell.com/outgoing/imanager273idm361.zip
           => `imanager273idm361.zip'
Resolving ftp.novell.com...
Connecting to ftp.novell.com||:21... connected.
Logging in as anonymous ... Logged in!
==> SYST ... done.    ==> PWD ... done.
==> TYPE I ... done.  ==> CWD /outgoing ... done.
==> PASV ... done.    ==> RETR imanager273idm361.zip ... done.
Length: 149,585,409 (143M) (unauthoritative)

 6% [=========>

2009-06-18 23:18:06 Jobs:0 Err:0
2009-06-18 23:30:06 Jobs:0 Err:0
ab@mybox0:/tmp> screen -x

2009-06-18 23:18:02 Jobs:0 Err:0
ab@mybox0:/tmp> wget ftp://ftp.novell.com/outgoing/imanager273idm361.zip
--23:18:05--  ftp://ftp.novell.com/outgoing/imanager273idm361.zip
           => `imanager273idm361.zip'
Resolving ftp.novell.com...
Connecting to ftp.novell.com||:21... connected.
Logging in as anonymous ... Logged in!
==> SYST ... done.    ==> PWD ... done.
==> TYPE I ... done.  ==> CWD /outgoing ... done.
==> PASV ... done.    ==> RETR imanager273idm361.zip ... done.
Length: 149,585,409 (143M) (unauthoritative)

100%[=========================================================================================>] 149,585,409   11.22M/s    ETA 00:00

23:18:19 (10.74 MB/s) - `imanager273idm361.zip' saved [149585409]

2009-06-18 23:20:19 Jobs:0 Err:0
ab@mybox0:/tmp> exit

[screen is terminating]
2009-06-18 23:31:22 Jobs:0 Err:0

As you can see at first I ran ‘screen -q’ and then I was in the application at an otherwise fairly normal shell. Next I ran my wget command as before but then something weird happened and the terminal/console displayed ‘[detatched]’. This was not just a coincidence. Right before that happened I pressed Ctrl+a followed by ‘d’ which, according to the ‘screen’ man page, has the following explanation: ‘C-a d (detach) Detach screen from this terminal.’ By using this key sequence I have just been bounced back to my original shell where I was before running screen (which created a new shell within the ‘screen’ application).

From here I can do just about anything, including log out of the system, without affecting my download. The beauty of the application is that I can not only disconnect and not affect things (essentially the same thing that backgrounding a process does) but I can reconnect and check on its progress or continue with more commands. This is shown where I typed ‘screen -x’. The screen command uses ‘-x’ to reconnect to an existing screen in multi-user mode (vs. -r to just reconnect) (prompting you for which screen to join if there are multiples) and then shows that entire session in your shell. As shown I see everything including the start of my download and its completion (which is why the timestamps from my shell are out of order between joining/exiting the screen). This is the basic start of what screen does as we’re basically enhancing what we can do with one command on our own which is useful but far from the end of the story. Keep in mind that it’s possible to use the original shell after detaching from the screen for whatever purpose so while you may be SSH’d into a system once you can start a screen, do something, detatch, and do something else without interfering with the first task. This can be done repeatedly with multiple screens or with multiple ‘windows’ in multiple screens as I’ll show later.

Going to the next example let’s pretend I need to help a coworker, or I want to access a system that a customer is already in without having them lose their environment variables from the middle of their test. If I can access the system as the same user they are using then I can join a screen that they previously started and are now in, all while they are still in it.

The result is something like using Elluminate, Bomgar, webex, or a similar application, but with just a shell. For those familiar with the Novell Remote Manager, RConJ, rconsole, rconip, or FreeCon (or similar) utilities for NetWare which would essentially let multiple users on the same system see the same output simultaneously and remotely ‘screen’ lets you do the same thing so you can see each other’s work (Linux natively, and by default, lets you do the same thing without interfering with one another). This really becomes nice when there are not alternative remote-access utilities around. It’s very nice when a network connection on one side or another is slow or has high latency where full graphical sharing environments do not perform well. With a text-based interface (SSH for example) for the various participants each change to the display sends just the amount of data needed to show that one change. There are no moving windows to draw, popups to distract, etc. As the other party types on the screen all are shown the updates. By default everybody in the session can also manipulate the text in the session as if they were on their own. In my own life this is useful when I have broken my system and need help from developers to undo the mayhem I have inflicted upon myself. As they SSH to my box and share their session via screen I can learn by watching, and even help them get around my system at any time if desired. There is not a great way to show this except with the same commands as were shown above. The windows that the two users would see would be identical so I’ll let you try that one on your own. One thing to note is that if EITHER user types ‘exit’ the screen will close entirely and both users will be dumped back to the shell from which they connected to (or launched) screen. To disconnect leaving the screen intact be sure to use Ctrl+a followed by ‘d’.

Consider another possibility where you just always have a bad connection to a machine and you need to not have something break when the connection finally blips into nonexistence. ‘screen’ is made to be attached-to and detatched from and by default it will detatch anybody who isn’t there and keep on chugging. This means that if you use screen consistently you never need to lose changes to a file being edited, or your place in a manual or other document being read either voluntarily or accidentally.

The final situation gives the most room to show off other features which is why it is saved for last. Being somewhat particular about my systems’ operations I started watching my logs regularly (continually) and originally it was just the /var/log/messages file. As I was just tailing the file eventually I had to disconnect, lose my place, reconnect later, and then try to find where I was in this fairly large file. Using screen easily solves that problem since I can just SSH in, reconnect to my screen, and see which lines on my display are newer than the last time I was perusing. So far, so good. Some other issues cropped up on me and it became important for me to watch other aspects of my system such as disk space, memory utilization, my ability to ping the outside world, and I started liking to have ‘top’ just run and show me what was taking up my processor. This is still solvable by the same option of running screen over and over and trying to remember to which I would want to connect for which service, but it requires me to have a bunch of windows open on my client machine or it requires me to detach and reattach to various screens when I just want to look at something quickly with as few keystrokes as possible. Checking the ‘screen’ man page the concept of a window (within ‘screen’) is what we will discuss next. Demonstrating this without a video is tricky but I’ll try to do my best.

First, let’s isolate the commands to be used for system monitoring:

#Watch my top processes, refreshing once per second (from the default of once every three seconds)
top -d1
#Tail my system syslog output located in /var/log/messages filtering out 'STATS:' because I don't care about them and I'm too lazy to make it both STATS: and syslog-ng
sudo tail -f /var/log/messages | grep -v 'STATS:'
#Run a script I have that simply executes 'free -l' and 'df -h' every minute or so indefinitely to keep track of disk space and memory utilization
#The script is literally the following after the shebang line: while [ 1 ] ; do free -l && echo '' && df -h && echo -e "\n\n"; sleep 60; done
#For those not familiar with bash at all it basically says, while true (forever), run free -l, print a blank line, run df -h, print two blank lines, wait a minute and repeat.
#See if I can ping Google since they have better uptimes than I do on my little boxes
ping google.com
#Watch my firewall log for anything strange
sudo tail -f /var/log/firewall
#Watch my ndsd.log for my eDirectory instance in case something strange shows up in it that I should know about
sudo tail -f /var/opt/novell/eDirectory/mytree/myserver-a/log/ndsd.log

So what I have above is a set of several commands I want to have running all the time that I can check and do so quickly and without worry of a network disconnect. Ideally I’d like these to run 24×7 (they don’t take much processing time) and be as simple to start as possible. In my case starting these up isn’t a big deal since the servers only reboot when a Service Pack (SP) for SLES comes out and I reinstall everything; however, having everything start as easily as possible would be ideal. Let’s see how screen handles this currently. I’m going to steal a paragraph from the ‘screen’ man page to define what I’d like to demonstrate:

When screen is called, it creates a single window with a shell in it (or the specified command) and then gets out of your way so that you can use the program as you normally would. Then, at any time, you can create new (full-screen) windows with other programs in them (including more shells), kill existing windows, view a list of windows, turn output logging on and off, copy- and-paste text between windows, view the scrollback history, switch between windows in whatever manner you wish, etc. All windows run their programs completely independent of each other. Programs continue to run when their window is currently not visible and even when the whole screen session is detached from the user’s terminal. When a program terminates, screen (per default) kills the window that contained it. If this window was in the foreground, the display switches to the previous window; if none are left, screen exits. Shells usually distinguish between running as login-shell or sub-shell. Screen runs them as sub-shells, unless told otherwise (See “shell” .screenrc command).

The first thing to note is that screen normally creates a single window. If you just load screen on accident one day you won’t even realize there are windows there for manipulation so getting started does not mean having to learn every feature of the application.

To see which window you are in let’s use the window number which starts out at (unsurprisingly) zero. So from within screen press Ctrl+a followed by 0 (zero). Notice that at the bottom of your terminal the following text is shown: ‘This IS window 0 (bash).’. As you can see you are in window zero already and you have the window named bash because that was the default application launched when you loaded screen. From here on out I’m going to use the notation that ‘screen’ uses in its man page for simplicity. Ctrl+a followed by 0 is simply C-a 0. Note that case does matter like it does in any computer. Adding additional windows within the same instance of screen is an easy task. Press C-a C-c (Crl+a followed by Ctrl+c). You may notice a slight flicker of your terminal and now you are in window number one. Test out changing to the current window to get the same prompt as before but telling you that you are already in window 1 with C-a 1 (Ctrl-a followed by 1). You can change to zero with C-a 0 and back with C-a 1. You will not get the note that you are already somewhere unless you really are already there. Just to keep on testing let’s create four more screens by pressing C-a C-c four more times. Once completed you should be able to use C-a followed by zero through five to change to any of the six windows within this one screen session. Notice that if you try a numeral that is not valid (six through nine currently) you get a listing like the following (you also get this list when you do C-a C-w):

0$ bash  1$ bash  2$ bash  3-$ bash  4$ bash  5*$ bash

What this is telling you is that you have windows zero through five, they are all named ‘bash’ (we’ll get to names more later), I am currently on window five (denoted by the ‘*’) and I was previously on window 3 (denoted by the ‘-‘). Your own results may be slightly different but if you were to push C-a 3 followed by C-a 5 and you had six default windows in the screen then your results should match my own. At this point let’s prove these are all within the same screen by detatching (C-a d) and then re-attaching (`screen -x`). Now we should be back in ‘screen’ so press C-a C-w we should see the listing of screens again. Showing the screen sharing with others again if you open up another terminal somewhere (or access the box as this user remotely) and then run `screen -x` now you are in the same screen with the six windows as the other terminal. You may quickly find that your display does not always match that of the other user which is important to note. While screen lets both people on the same window (remember, only one window is created by default within screen) see the same things and type in the same area screen does not, at least by default, ensure that everybody within screen is on the same window. Also when you have multiple windows typing ‘exit’ in one of them closes bash within that window (assuming you’re in a shell that the ‘exit’ command closes and ‘bash’ was what was opened in the window originally) and does not close the entire screen. Screen only exits when all of the windows within it are closed. When you finally exit screen you should always get the note that screen is terminated so that should help in recognizing that particular event vs. just a single window closing.

So we have a screen with six windows in it. Now let’s see if we can get all of my commands in there so we can actually use the thing for something useful. Go to window zero (C-a 0) and type in the first command from my list above. Go to window one (C-a 1) and enter the second command. Continue through the last one. For the script you can either create your own script in your own ~/bin directory or you can just input the while loop directly and have it run. For the ndsd.log file either run all of these on a machine with eDirectory or choose another log file of interest to you which you can tail. When completed experiment with connecting, disconnecting, and changing among the various screens to see what is going on. Another interesting set of keystrokes is C-a C-a which takes you to the previous window. Using it over and over switches you back and forth between two windows indefinitely.

Once that is completed you may find that using Ctrl-a 6 gives you the usual list of windows but still with no details to help you differentiate one window from another except for their identifying integer. While each one started with ‘bash’ they are all doing their own thing now and a name that YOU care about would be nice. Thankfully the ‘screen’ devs are smart folks. Try the following, noticing the change of case: C-a A After pressing Ctrl+a and then ‘A’ (capital A) the bottom of the terminal changes and lets you enter a name for the window. Backspace over the old name and put in something descriptive for you. For window zero perhaps ‘top’ would be appropriate. Go to the next window (C-a 1) and then change its name (C-a A) to something you prefer. Do this for all your windows. Disconnect, reconnect, and see the window names stick. This removes a lot of ambiguity from life which is nice for our particular task. With window names in place you can also switch to windows by name using C-a ‘ (yes, Ctrl+a followed by a single-quote or tick) and then entering the name of the window you would like to try.

For navigation there are a few other tricks that are worth mentioning. Iterating through windows is trivial in a number of ways. From the man page:

C-a space
C-a n
C-a C-n     (next)        Switch to the next window.

On the other hand to go backward in the order:

C-a backspace
C-a h
C-a p
C-a C-p     (prev)        Switch to the previous window (opposite of C-a n).

Switching through windows like crazy you may eventually forget where you are. Find out:

<prec -a N (number) Show the number (and title) of the current window.

Also at some point you may see a message from screen at the bottom of the terminal that goes away before you get a chance to read it. That’s not a problem:

C-a m
C-a C-m     (lastmsg)     Repeat the last message displayed in the message line.

Screen also has the ability to write its contents (display) to a file, read in data from a file, change modes (to command mode to give complex commands to ‘screen’), change key bindings, lock (password-protect) windows, and basically customize itself to be flexible for you. See the man page for full details on all of the interactive options but be aware they are, out of the box, extensive. Now that we’ve covered working within screen to a decent extent what about using screen from the command line? There are numerous ways to work with screen before even getting into the application itself including ways to start it without attaching, starting it and running applications automatically, and doing various other things. Some of the options I have found useful (so far) follow.

First let’s start with some information gathering. The ‘screen’ command has a -list (-ls) parameter that will show available screens for the current user. Here is some sample output from various invocations when having no screens, one screen, and two screens running for the current user:

ab@mybox0:~/Desktop> screen -list
No Sockets found in /var/run/uscreens/S-ab

2009-06-19 10:22:25 Jobs:0 Err:1
ab@mybox0:~/Desktop> screen -ls
There is a screen on:
        31435.pts-3.mybox0   (Attached)
1 Socket in /var/run/uscreens/S-ab

2009-06-19 10:22:29 Jobs:0 Err:1
ab@mybox0:~/Desktop> screen -ls
There are screens on:
        31435.pts-3.mybox0   (Detached)
        31447.pts-3.mybox0   (Attached)
2 Sockets in /var/run/uscreens/S-ab

2009-06-19 10:22:35 Jobs:0 Err:1

So from here we can see that my user has two screens. One is attached-to by a user, and another is not attached-to at all. The first column there is actually the PID of the screen process for those interested in seeing that directly since that can be useful sometimes. The `pstree -p` output also shows my screens and the multiple windows underneath them:

So what is interesting is when you have multiple screens the `screen -x` command takes a little more to actually join a screen. When there is one screen to join it joins it but when there are multiples it needs to be told which to join. By default using the PID is easy enough. It may be more convenient at times, though, to have names for connecting to a screen just like you do with changing to a window within a screen. Let’s do a test by creating a new screen named ‘testme’ after closing my previous screens out.

screen -S testme

When I detach and get a new listing this is now what I see:

ab@mybox0:~/Desktop> screen -ls
There is a screen on:
        5358.testme     (Detached)
1 Socket in /var/run/uscreens/S-ab

That’s neat and all but the real use is when we re-attach. No longer are we using things like `screen -x 5358` though that still works; I can directly connect to the screen using the name that makes sense to me (the mere human) instead of the number that makes sense to the computer:

screen -x testme
So what if we want to go to a specific window within a specific screen, all by names or numbers?  All possible.  The new switch is -p and takes either the number or the name.  If you have duplicate names it takes the last one you were in (if applicable and the system knows, since it only knows about two recent windows at most) or else the first one in the list, in my experience, so use a number if that is your situation for some reason, or just get unique names:
screen -x testme -p windowname
screen -x testme -p 3

Either command above works the same way if window ‘3’ is also named ‘windowname’. If they are different windows then connecting works to whichever window is specified. Doing this you can connect to your screen named ‘monitor’ and your window named ‘top’ from anywhere quickly without having to pull up a list of screens, then a list of windows, etc. Another option that is useful for starting things up (automating our ‘monitor’ screen on system startup) is to have a .screenrc file for a given user. For my user, for example, I may want the following ~/.screenrc file to exist:

screen -t top top -d1
screen -t messagesmon sudo tail -f /var/log/messages | grep -v 'STATS:'
screen -t spacemon /home/ab/bin/spacemon.sh
screen -t pinggoog ping google.com
screen -t fwmon sudo tail -f /var/log/firewall
screen -t edirmon sudo tail -f /var/opt/novell/eDirectory/mytree0/myserver0-a/log/ndsd.log

This tells ‘screen’ to load up six windows every time it is loaded. This is not really what I want but it shows how you can customize screen for every time it loads. Instead, for my purposes, I would probably create a /home/ab/.screenrcmonitor file and then I would have a script load when the system started that essentially ran the following:

sudo -u ab screen -d -m -S monitor -c /home/ab/.screenrcmonitor

This tells screen to run as my user and start a new screen named ‘monitor’ using my customized screen config file as the way to load up screen. The only thing left to do here is to make sure /etc/sudoers is setup so that my user can run these commands without being prompted for the password. If I am prompted for the password I can enter them the first time I join the screen but that means the commands are not really running until I login and set that up. Putting the sudo command above in an init script would be fairly trivial and could then just be set to be the last thing to load with my system. One more consideration at this point may be that anybody who hacks my account suddenly has the ability to tail the firewall, messages, and eDirectory log files which would normally require ‘root’ privileges. Blocking that is fairly easy to do as well using a crypt-ed password in the same screen configuration file with the following line for the password ‘password1’:

password OHBn.WKL7Xtrc

With this line at the end of the screen configuration file attaching to the screen will just require the password to be entered once before access is granted. I was hoping to find a way to do that in the configuration file per window (and not for the entire screen) but haven’t worked that out yet so if anybody finds it please share the wealth.

The password option leads nicely into the last section I’d like to cover. Normally screen is nice for sharing with others who have your username and password on a system (often, ‘root’ for administrative things at least), or at least can become your user somehow which could include any root-enabled user. It may be useful, though, to share a screen with a user who does not have the ability to become your user somehow. As luck would have it ‘screen’ has this ability built in as well. I’ll only cover doing this on the fly since that makes the most sense to me but feel free to check the man page for other information as you need it.

First, there is a change I needed to make in the system for this to work. The ‘screen’ command needed to be SUID’d which means whomever runs it now runs it as the user owning the executable (by default this is ‘root’). For the security conscious this means giving the process, and anything it lets the user do, full power over the system. Thankfully ‘screen’ is written well and I do not know of any bugs that would let a user do just anything unless they were already ‘root’ but keep in mind that this change must be done. The simplest way to change the default permissions on my SLED box from r-xr-xr-x (0555) to r-sr-xr-x (4555) is with the following command:

sudo chmod 4555 /usr/bin/screen

After entering the ‘root’ password the file is now set to run as ‘root’ when invoked. Once done we can startup our screen and use a new set of control sequences, namely C-a : (Ctrl+A followed by colon (:)). This puts you in ‘command mode’ within the screen and from here we will enter some commands, substituting in values as needed. In my case I am SSH-ing to my machine as the ‘guestuser’ user whom I want to join my (ab) user’s screen. You will need to, as the owner of the screen, run C-a : before each of the following commands:

multiuser on
acladd guestuser OHBn.WKL7Xtrc

Once that is done the user ‘guestuser’, now on the system having the ‘screen’ command with the SUID bit set along with you, runs the following:

screen -x ab/monitor

Remember that my screen was named ‘monitor’ which is why it is shown above. The alternative would be to use the long format for the same as mentioned in the documentation:

screen -x ab/15875.pts-3.mybox0

The longer format is available running as the original user with the ‘-ls’ switch but basically it’s a lot easier to have a named screen. Either way after running the command the user (guestuser) is prompted to enter a password which you give them to enter. Keep in mind that you are essentially letting anybody who joins your screen have as much power as you have by default. ‘acladd’ is made to give the user power to join and essentially full privileges from there. The password parameter on the end (which still uses a crypt’d password) is optional so you can omit it if that suits you. To restrict permissions there is an extensive set of ACLs possible with the ‘chacl’ command. Some examples include the ability to give only read rights (only see the screen), write rights (ability to type in the screen) and execute rights (the ability to execute commands to the screen like you did to enable multiuser and to add a user to join your screen). The abilities are more-granular than that but this is all getting into somewhat advanced stuff that probably isn’t needed by most individuals. For a great set of instructions see the man page.

Hopefully this helps you get started with another great command in Linux. This is available in source version so feel free to add it to other systems. Even windows can benefit from screen if you have something like cygwin installed, though by default there is no ‘screen’ there to enjoy. If you have ways that you use screen please feel free to add comments at the bottom. If you have other commands or switches that make your lives easier share them so others may learn as well, or write your own notes and post them on this same site for free. Suggestions for other tips/tricks are welcome as well. Enjoy.

(Visited 1 times, 1 visits today)

Leave a Reply

Your email address will not be published. Required fields are marked *

No comments yet