Generate SFTP Checker

Batch file to generate batch files to check for missing files on an SFTP server.

Needed a simple solution to repeatedly check an external SFTP server for a missing file to become available. This is a regularly occuring problem for me with different files/servers each time. This batch file asks for server name, username, password, etc, and then writes a customized “WaitFor_%filename%.bat” file to run, along with a helper file “WaitFor_%filename%_FTPCommands.txt” to store the FTP commands. The FTP commands are ran through psftp.exe. When the file is found and downloaded, a big red command-prompt popup will be made to get your attention.

Instructions

  1. Download and install PuTTY https://www.chiark.greenend.org.uk/~sgtatham/putty/latest.html
  2. Ensure psftp.exe from the PuTTY install is in your PATH
  3. Run the batch file, answer the questions
  4. Run the generated batch file customized to your answers to begin waiting. Be responsible with your passwords!

Source

GitHub: https://github.com/kriscode1/GenerateSFTPChecker

GenerateSFTPChecker.bat:


REM GenerateSFTPChecker.bat
REM Kristofer Christakos
REM Batch file to generate batch files to check for missing files on an SFTP server.
REM https://kriscoder.com/, https://github.com/kriscode1/
REM License: Modify as desired.

@echo off
set /P servername="Enter the SFTP server name: "
set /P username="Enter the username: "
set /P password="Enter the password: "
set /P directory="Enter the directory of the file to wait for (or leave blank for default directory): "
set /P filename="Enter the filename to wait for: "
set /P seconds="Enter how often you want to check (in number of seconds): "

set batchname="WaitFor_%filename%.bat"
set helpbatchname="WaitFor_%filename%_FTPCommands.txt"

IF EXIST %batchname% del %batchname%
IF EXIST %batchname% echo Error clearing %batchname%&pause
(
echo @echo off
echo :loop
echo psftp %username%@%servername% -pw %password% -b %helpbatchname%
echo IF NOT EXIST %filename% GOTO :sleep
echo start "Available File!" cmd /c "color C0&date /T&time /T&echo File %filename% is downloaded.&pause"
echo exit
echo :sleep
echo timeout /t %seconds%
echo goto :loop)>>%batchname%

IF EXIST %helpbatchname% del %helpbatchname%
IF EXIST %helpbatchname% echo Error clearing %helpbatchname%&pause
IF DEFINED directory echo cd %directory%>>%batchname%&timeout /t 1 /nobreak>nul
echo get %filename%>>%helpbatchname%

echo Done.
pause

PingTest

Tracks remote disconnections by parsing and recording output from Microsoft’s ping utility.

Usage

Call pingtest(target_name, ping_count) to run the command “ping target_name -n ping_count“. Then pingtest() will return a list of times for each ping_count pings.

  • time == -1 is error
  • time == 0 is when ping <1ms
  • time >= 1 is the time in milliseconds

This was written to test various servers for network connectivity, continously, and discover if any disconnections occured while away. The times were logged with a little formatting to a TSV file to be sorted and check for any -1 time values (disconnections). An example of this is in pingtest.py. Example output below, pinging each target twice:

2017-10-12 19:39:00.817122      192.168.0.1     2       2
2017-10-12 19:39:01.849578      google.com      14      14
2017-10-12 19:39:02.910976      10.60.17.103    -1      -1
2017-10-12 19:39:11.848790      209.88.198.133  153     160
2017-10-12 19:39:13.035685      192.168.0.1     1       1
2017-10-12 19:39:14.067242      google.com      14      12
2017-10-12 19:39:15.108837      10.60.17.103    -1      -1
2017-10-12 19:39:23.848390      209.88.198.133  150     148
2017-10-12 19:39:25.010907      192.168.0.1     1       1
2017-10-12 19:39:26.035119      google.com      13      16

Source

GitHub: https://github.com/kriscode1/PingTest

pingtest.py:


'''Contains pingtest(), to run Microsoft's ping utility and parse output.
Tracks remote disconnections by parsing and recording output from Microsoft's ping utility.

Kristofer Christakos 2017
'''

import subprocess    #For running Microsoft's ping utility in pingtest()
import datetime        #For outputting time in log file, not needed in pingtest()
import sys             #For the tests only, not needed in pingtest()

def pingtest(target_name, ping_count):
    '''Runs "ping target_name -n ping_count" and parses output as list of times.
    Time -1 is error, 0 is when ping <1ms, and time >=1 is in milliseconds
    '''
    process = subprocess.run("ping " + target_name + " -n " + str(ping_count), stdout=subprocess.PIPE)
    result = process.stdout.decode("utf-8").splitlines()
    times = []
    for line in result[2: 2 + ping_count]:
        time_index = line.find("time")
        if (time_index == -1): 
            times.append(-1)    #Request timed out.
            continue
        time_index += 4
        space_index = line.find(" ", time_index)
        if (space_index == -1):
            times.append(-1)    #Error
            continue
        if (line[time_index] == '='): time_index += 1
        time_str = line[time_index:space_index]
        if (time_str == "<1ms"): times.append(0)
        else:
            time = time_str[0:-2]
            try:
                times.append(int(time))
            except:
                times.append(-1)
    return times

def get_timestamp():
    return str(datetime.datetime.now())

def join_ints(int_list, separator):
    '''Inputs list of ints and joins them to a string.
       Example input:    [1, 2, 3, 4], '\t'
       Example output:   "1\t2\t3\t4"
    '''
    return separator.join([str(n) for n in int_list])

def pingtest_and_write(server, file_handle=None, yes_print=True):
    '''Perform pingtest, then do custom formatting and write to log and print.'''
    timestamp = get_timestamp()
    times = pingtest(server, 2)
    writestr = timestamp + '\t' + server + '\t' + join_ints(times, '\t') + '\n'
    if yes_print: print(writestr, end='')
    if (file_handle != None): file_handle.write(writestr)


### Run some tests using the above functions ###

f = None
if (len(sys.argv) == 2):
    try:
        f = open(sys.argv[1], "a")
    except:
        print("Could not open file to log to.");
        sys.exit()
else:
    print("Enter a single parameter to store log file. Currently not logging.")

while(True):
    pingtest_and_write("192.168.0.1", f)
    pingtest_and_write("google.com", f)
    pingtest_and_write("10.60.17.103", f)
    pingtest_and_write("209.88.198.133", f) #GreenTeamDNS 2
    f.flush()

 

Memory Warden

Click here to jump to the latest download below.

Description

Memory Warden monitors your system memory usage (% RAM). When usage gets too high, this program can alert you and optionally close programs to prevent computer lockup, even when your mouse cursor is not responding.

This program was written for people without the time or technical expertise to investigate why their computer is slowing down (such as busy stock traders). To accommodate all levels of urgency and autonomy, there are four different actions that may be set at any percent level:

Actions

(1) quiet background warning
A passive warning that will not interrupt whatever you are doing.

(2) alert with pop-up warning
An aggressive warning that will pop-up over any windows you have open.

(3) end memory hogs from kill list
Will automatically close programs, but only those from a list that you build.

(4) end any memory hogging program
Will automatically close any program.

To prevent a program from being automatically closed, add it to your Safe List in Advanced Settings.

Suggested Use

Create warnings (using actions 1 & 2 above) for when your memory begins getting high (70% to 85%). This will give yourself a chance to gracefully close programs. The warnings will tell you who the memory hogs are.

When system memory usage reaches 95% or higher, have Memory Warden automatically end the memory hogs in case your computer begins locking up (using actions 3 & 4 above).

Using the Kill List

Some users will want Memory Warden to automatically close specific programs before others. You can do this with the preferred kill list in Advanced Settings. For example, add Chrome, Internet Explorer, and Firefox to this list. Then use action “(3) end memory hogs from kill list”.

Installation

Memory Warden is installed for all users or the current user with an MSI file. Download the latest version below.

Old versions will be uninstalled by new versions automatically.

Prerequisites: Microsoft .NET Framework version 4.5.2.

If Memory Warden fails to run because this is not installed, download and install the framework from Microsoft’s website here: https://www.microsoft.com/en-us/download/details.aspx?id=42642

License and Source Code

Please read the EULA before installing: MemoryWardenLicense.rtf

Program written in C# with WPF and WiX for the installer. Up-to-date source code is currently not public. Feel free to message me with questions or comments at kriscode1project@gmail.com or https://kriscoder.com/contact/

Latest Version

Memory Warden v1.3 download (624 KB): MemoryWarden-v1.3.0.msi

Released: 2017-10-12

Change Log:

  • New window to view current usage and processes — access by left clicking or right clicking the tray icon
  • Counts per-process full working set memory instead of private working set memory (improvement over Task Manager) — results in more accurate detection of memory hogs
  • UI: settings window now remains on top, so it is unlikely to unintentionally leave it open and miss warnings
  • Bug fix: all old logs were deleted, now logs are deleted only if older than 30 days
  • Bug fix: Windows shortcut alt+tab would bring up an inactive settings window
  • Bug fix: duplicate instances running on startup

Old Versions

Memory Warden v1.2.1 download (624 KB): MemoryWarden-v1.2.1.msi

Released: 2017-10-02

Change Log:

  • Smart all-user vs current-user automatic startup
  • Added versioning to user settings
  • Removed redundant error messages
  • Fully revamped installer
  • Support for easy upgrades in future
  • Bug fixes

Memory Warden v1.1 download (1.45 MB): MemoryWarden-v1.1.0.0-x86.msi

Released: 2017-09-27

Change Log:

  • Default install directory & registry path changes
  • Fix refreshing RAM when hovering over tray icon
  • Disable Cancel button if program wasn’t running
  • Upper right X button now minimizes to tray

Memory Warden v1.0 download (1.45 MB): MemoryWarden-v1.0.0.0-x86.msi

Released: 2017-09-24

Auto Clicker for Clicker Heroes

Purpose

Clicks over and over again while playing Clicker Heroes in Chrome.

Safety features:

  • Caps Lock must be toggled on
  • Active window title must contain “Clicker Heroes” and “Google Chrome”

Prevent resource hogging:

  • Only checks for Caps Lock once per second.

Code

AutoClicker.py

'''Clicks over and over again while playing Clicker Heroes in Chrome.
Safety features:
Caps Lock must be toggled on
Active window title must contain "Clicker Heroes" and "Google Chrome"
Prevent resource hogging:
Only checks for Caps Lock once per second
'''

import win32api, win32con, win32gui
#python -m pip install pypiwin32
import time

while True:
    time.sleep(0.01)
    capsLockOn = win32api.GetKeyState(win32con.VK_CAPITAL)
    if (capsLockOn == 1):
        topWinText = win32gui.GetWindowText(win32gui.GetForegroundWindow())
        if (("Clicker Heroes" in topWinText) and 
            ("Google Chrome" in topWinText)):
            win32api.mouse_event(win32con.MOUSEEVENTF_LEFTDOWN, 0, 0, 0)
            win32api.mouse_event(win32con.MOUSEEVENTF_LEFTUP, 0, 0, 0)
    else:
        time.sleep(1)

Download

Download AutoClicker.py

Download Executable Version (no Python needed)

License: MIT License: software as is, no warranty, and this license must always be included with this software:
https://kriscoder.com/licenses/MIT-LICENSE.txt

GitHub: https://github.com/kriscode1/AutoClicker

Build Info

Written in Python 3. Uses pypiwin32, so install “python -m pip install pypiwin32“.

To create the executable, I wrote a py2exe setup script. For more information on this technique, see How to Convert Python Scripts to Executables.

Usage

The Python script runs in a loop, so you can simply download and double click the .py file, or run it from command prompt using:

python AutoClicker.py

The executable can be ran without Python installed.

After opening the program, you will see a blank prompt. Then:

  1. Navigate to your Google Chrome window with Clicker Heroes open.
  2. Point your mouse where you want to click a lot.
  3. Turn on Caps Lock.

If you turn off Caps Lock, or the cursor is moved and it clicks off the browser window, then the script will stop clicking.

Disclaimer

Use at your own risk. If the script is against Clicker Heroes’ policy, then don’t use it. Also, don’t run any auto clicker program while you have important documents open or buttons that you shouldn’t click. The safety feature takes one bad click to activate. Feel free to modify the code however you’d like for yourself.

PropReports Assistant

Description

Creates links for visiting PropReports pages, one month at a time.

This tool makes viewing monthly reports easier by building the links for each month instead of having to manually chose the start and end dates for each month on the website.

Usage

  1. Sign into PropReports and copy-paste the URL into the program. This tells the program what the links should look like.
  2. Fill in the date range of when you would like monthly reports, as well as your report type settings.
  3. Press the Generate Links button to see a list of links for monthly reports. Click on the links.

Installation

No installation required. PropReports Assistant is a stand-alone executable. You may have to update your .NET runtime assemblies. If so, run Windows Updates, or let me know.

Language

Program written in C# with .NET and WPF.

Updates

If PropReports modifies their website, the generated links may not work anymore. If so, let me know and I’ll release a fixed version.

GitHub: https://github.com/kriscode1/PropReportsAssistant

Releases

Release Date: 2017-05-01
Initial release.

Download Program: PropReportsAssistant.exe
Source Code: source.zip
GitHub: https://github.com/kriscode1/PropReportsAssistant

Conclusion

You now know enough Command Prompt to take the next step towards more of your goals and dreams. Continue exploring command line arguments and reading command line program documentation. Search online for more resources when uncertain. If you begin programming, I highly recommend learning to compile or run your programs with Command Prompt. You might find yourself writing your own commands or Command Prompt scripts some day.

If you have any feedback, please email me or post comments below.


Prev

Absolute Paths

You’ve now learned most of what you need to know to use Command Prompt effectively. This lesson will be relatively simple compared to the previous. You may have already figured out what’s on this page by playing around with Command Prompt on your own. To make efficient use of Command Prompt, you’ll want to know the difference between absolute and relative paths.

Up until now, we have been using relative file/folder paths. When you change your working directory to get to a file, you are using relative paths. Absolute paths are paths that reference a file/folder regardless of your working directory.

For example, if your working directory is C:\Windows\System32, then:

  • Typing notepad.exe is a relative path to Notepad
  • Typing C:\Windows\System32\notepad.exe is an absolute path to Notepad

In the above example, both methods reference the same file. But the benefit of absolute paths is that you don’t need to change your working directory. Therefore your working directory could be C:\User\somename\Documents, and you could run Notepad by typing C:\Windows\System32\notepad.exe without changing the working directory first.

If you need to browse and use commands like DIR, then you obviously don’t need absolute paths. However if you know exactly where your file/folder is, then absolute paths will save you time. There is also necessary utility in absolute paths, which you will learn by the exercise below.

Copying Files to New Folders

Here is an example of when you would want to use absolute paths. Suppose you need to use the COPY command to copy archive.zip from the folder C:\User\Administrator\Documents to a flash drive assigned as F:, and you want it in your F:\backups folder on the flash drive. There are three straightforward ways to do this:

Method 1:

  1. Change your working directory to C:\User\Administrator\Documents
  2. Run copy archive.zip F:\backups\archive.zip

Method 2:

  1. Change your working directory to F:\backups
  2. Run copy C:\User\Administrator\Documents\archive.zip archive.zip

Method 3:

  1. Don’t change your working directory at all
  2. Run copy C:\User\Administrator\Documents\archive.zip F:\backups\archive.zip

Method 1 uses a relative path for the file source, and absolute for the file destination.

Method 2 uses an absolute path for the file source, and relative for the file destination.

Method 3 uses absolute paths for both the file source and destination, because the working directory wasn’t changed.

Which method you use doesn’t matter. What’s important is getting a feel for the different ways you can use Command Prompt to complete your objectives as quickly, intuitively, and comfortably as possible.

Shortcuts

Tab

The most useful keyboard shortcut for typing paths quickly is the tab key. If you haven’t already, try pressing the tab key multiple times at a blank prompt. Command Prompt will cycle through the file/folder names in your current working directory. You can also press shift+tab to cycle the opposite direction.

This works while you’re in the middle of writing a name too. Try typing half of a file name and then pressing tab to autocomplete the name.

Tab is also helpful when typing long absolute paths. For example, instead of typing the full line cd "C:\Program Files\Internet Explorer", try just typing (all on one line):

  1. cd C:\Pro TAB, to get cd "C:\Program Files"
  2. \Inte TAB, to get cd "C:\Program Files\Internet Explorer"

Too easy!

Arrow Keys

When you want to re-type an old command, try using the up/down arrow keys to cycle through your current Command Prompt window history.

When you want to edit a command before pressing enter, try using the left/right arrow keys instead of pressing backspace a retyping everything.


Prev ————————– Next

PATH

You learned that you can run any executable in Command Prompt by: 1. changing your working directory, and 2. typing it’s name. But how are programs running when you type their name without changing the working directory? A good example of this feature is running Microsoft’s Notepad. You can run notepad without navigating to C:\Windows\System32 where notepad.exe really is.

Understanding the answer to this question is vital to saving you huge headaches in the future. The feature is called “PATH”, which you’ll now learn about.

Environment Variables

In Windows, every program running has an “environment”. A program’s environment comes with pieces of text called environment variables. You can see examples of these by running the SET command with no parameters. Run this command now and you’ll see variables for your username, your computer’s name, etc. Programs can access these values if the variable is inside their environment.

There are two types of environment variables:

  1. User environment variables (set for the current logged in user)
  2. System environment variables (set for every user)

By default, a program will inherit the variables of whatever program started it. The programs started when you first turn on your computer and log in will inherit both the system environment variables and then the user environment variables.

These variables are stored in something called “the registry” but the easy way to access them is using SET.

SET

The SET command allows you to make or change environment variables for the current command prompt window.

As stated above, you can see the environment variables that the current command prompt window has inherited by typing SET with no arguments. You can also see specific environment variables by specifying their name as an argument. For example, set userprofile will show you the path stored for other programs to know where your Documents, Music, etc are.

To make a new variable or change an old one, use SET [variable=[string]]. For example SET test=my test environment variable will add a new variable “test” to the list of variables you can see with SET.

Points to note:

  • This new environment variable is only known to that Command Prompt window. If you close Command Prompt and open it again, you will not see “test”.
  • While it is common practice to not have spaces in your environment variable names, you can specify spaces if you’d like.
  • Variable names are case insensitive. “TEST” is the same as “test”.
  • Using SET may be useful to you writing scripts in the future.

PATH

Now that you know about environment variables, we can talk about the environment variable PATH. I will write “PATH” in caps to mean the environment variable named “Path”, and not the generic word “path” for file/folder locations.

When you type SET or SET path you’ll see the PATH inherited value. It should be a long list of directories separated by semicolons ;. When you type the name of a program in Command Prompt, Windows will search all directories in your PATH variable for the program you’re trying to run. If it is found, then the program will run without you changing your working directory!

Notice C:\Windows\System32 is in your PATH by default. This is why you can run Microsoft’s Notepad from Command Prompt without changing your working directory.

Without PATH, you would either have to always change your working directory before running any program, or Windows would need to search every single folder in your file system (which could take a very long time). PATH is the compromise between the two extremes, limiting the search to a small number of directories.

Changing PATH Temporarily

To add a path to PATH without retyping everything already in PATH, you can use the Command Prompt variable syntax: %PATH%. Placing percent signs around an environment variable name will cause Command Prompt to look up the value and use the value instead.

For example, a quick way to change your working directory to your Documents folder is:

cd %userprofile%\documents

For me, the above is the same as typing cd C:\Users\Kristofer\Documents.

Therefore we can add directories to PATH with set path=%path%;DirectoryName. Some examples:

set path=%path%;C:\MyDownloadedPrograms
set path=%path%;C:\InstallDrivers1;C:\InstallDrivers2
set path=%path%;%userprofile%\documents

Try adding a directory to your PATH variable now. If you mess up, just close and reopen Command Prompt to get the default inherited environment back.

Changing PATH Forever

To change PATH permanently for every Command Prompt window you open in the future, follow these steps:

  1. Navigate to your System information page. You’ll see information on your version of Windows, processor, RAM, computer name, etc. There are several ways to do this:
    • Press the keyboard shortcut: Windows Logo Key + Pause/Break Key
    • Start -> right click “My Computer” or “This PC” -> choose “Properties”
    • Start -> Control Panel -> System and Security -> System
    • Enter the command “control system” in Command Prompt
  2. Click the “Change settings” link to open System Properties and click the Advanced tab.
  3. Click the “Environment Variables…” button near the bottom.

Now you can choose to modify either your user environment variables or system-wide variables. When any user on your computer opens Command Prompt (or any other program), first the system variables will be loaded, and then the user specific variables for that user. If you have (or create) a PATH variable for both the system environment and the user environment, then the user paths will be appended to the end of the system paths.

Think about whether you want other users running programs from the directory you want to add. If you add it to the system environment, then everyone will see the path in their Command Prompt window PATH variable. If you don’t want to affect any other user, then add/modify your user variable instead.


Prev ————————– Next

Programs

Until now I’ve been calling all things typed into Command Prompt a “command” (or the arguments following). In reality, most commands are not built into Command Prompt’s code, but instead separate programs that Command Prompt blindly runs on your command.

All programs can be ran from Command Prompt. Programs in Windows generally have a .exe file extension. If you navigate your working directory to a folder containing an exe, and you type that executable file name as a command, then the program will run.

To run any program, simply:

  1. Navigate your working directory to it
  2. Enter the program’s name

Console vs GUI

Most programs fall into the two categories:

  1. Graphical User Interface (GUI) with windows, buttons to click, etc
  2. Console/command line interface, only can be ran in a console window, outputs text

If you try to run a graphical program in Command Prompt, it will simply open up and ignore Command Prompt. Doing this is usually not desired, but it can be done. For example, Microsoft’s Notepad program has a GUI but it can also accept a file name argument. So you can enter notepad test.txt and notepad will open up and create a new file named test.txt in your working directory.

Conversely, if you ever double click a program and see a command prompt window flash up and go away very quickly, you know you’ve just clicked a console-based program. Open up Command Prompt and run the program there instead to see what text was outputted. Most readers probably have not seen this before. It’s not important to have seen this but if you’re curious:

  1. Open a GUI folder explorer window and click your way to C:\Windows\System32.
  2. Open “schtasks.exe”. This is your console-based scheduled tasks program that comes with Windows. It is only useful in Command Prompt since it outputs text and closes.
  3. In Command Prompt, CD to C:\Windows\System32 and run “schtasks.exe”. See what you just missed in step 2.

Prev ————————– Next

Arguments

You have typed in a few commands now, and some of those commands took additional information such as a file name. The additional pieces of information you type after a command name are called arguments, or synonymously in most contexts, parameters.

In Command Prompt, arguments are separated by spaces. When you type cd .. for example, Command Prompt splits the text you typed by spaces. The first piece of text is always taken as the command name. Every piece of text after that is taken as an argument. The argument is passed in a list to the internal programming of the command. If you ever write software or scripts, you will eventually learn how to get this list of arguments too.

Try typing help cd again. You will see something like:

Displays the name of or changes the current directory.

CHDIR [/D] [drive:][path]
CHDIR [..]
CD [/D] [drive:][path]
CD [..]

The above is documentation that Command Prompt users learn how to read. The CHDIR lines are telling you that CHDIR is another name for CD. More importantly, notice the argument [..]. The brackets usually mean the parameter is optional. But what is ..? If you read down, you’ll see:

.. Specifies that you want to change to the parent directory.

This help documentation is telling you that you can optionally type .. as an argument and it will work.

Now take note of the optional /D argument. What does that do? Well read down and you’ll see:

Use the /D switch to change current drive in addition to changing current directory for a drive.

Now we know! You can specify /D as an argument to switch from C:\ to D:\ or E:\ or wherever you may need to. This is useful if you want to change your working directory to a USB flash drive. Example: cd /D "X:\Travel Files".

Spaces

To force Command Prompt to ignore spaces and treat text as a single argument, put quotes around the argument. That is why you need quotes around file and folder names containing spaces.

Switches

Arguments such as /D above are often called switches. The argument can be thought of as switching an option on/off. File name arguments are not switches. You’ll often see switches start with a forward slash /, but sometimes a hyphen - or double hyphens -- too.

Switches typically are case insensitive. For simple commands, a good programmer should have remembered to check for both capitalized and lowercase arguments while looking at the argument list. Some very complicated programs have so many switches that they need capital and lowercase letters to be different things. Some programs are complicated enough to abandon the one-letter convention and use full words for switches.

Don’t be surprised to see variation. If you glance at the documentation you’ll be okay.

Help

Most commands will not have help text with HELP. Programmers typically include documentation inside their command, which you can access with the right arguments.

If you are stuck and want to see documentation, try specifying /? as an argument. For example, enter cd /? and you will see the same text help cd gave you. Some programmers use /h or -h to give help text instead.

Feel free to try these arguments for new commands. If you’re worried about what might happen when they don’t work, check online first. Testing unknown arguments in cd or dir might be harmless, but you would be a smart person to think twice before seeing how shutdown handles shutdown /?. For the record, shutdown /? does give you help documentation instead of shutting down your computer.


Prev ————————– Next