LogonUser + CreateProcessAsUser at Service = error 1314 - delphi

I have a windows service created with Delphi 7, with StartType = stSystem.
Now I need to launch an application to make some things for me.
This application has a MainForm and other GDI resources.
The parameter passed to the application assigns values for some controls (like edits and memos) and then click at a button....
I'm trying this:
var
token: cardinal;
si: TStartupInfo;
pi: TProcessInformation;
begin
if not LogonUser('admintest', '', 'secret123', LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, token) then
RaiseLastOSError;
try
if not ImpersonateLoggedOnUser(token) then
RaiseLastOSError;
fillchar(si, sizeof(si), 0);
si.cb := sizeof(si);
si.lpDesktop := PChar('winsta0\default');
if not CreateProcessAsUser(token, nil, '"c:\...\myapp.exe" -doCrazyThings', nil, nil, false, NORMAL_PRIORITY_CLASS or CREATE_NEW_CONSOLE, nil, nil, si, pi) then
RaiseLastOSError;
CloseHandle(pi.hThread);
waitForSingleObject(pi.hProcess, INFINITE);
CloseHandle(pi.hProcess);
finally
CLoseHandle(token);
end;
end;
When I run my service executable as a normal application (-noservice), it starts as a Forms.Application and creates a MainForm with a button "Start".
*The button runs the same code that service run, but it doesn't works and it's rasing the eror code 1314 at createprocessasuser.*
Why? What is the diference between SYSTEM service and a normal application launched by a administrator?
My environment is a Windows 7 Pro x64
What am I doing wrong?
How can I solve this?
Can someone post an example?

Error 1314 is ERROR_PRIVILEGE_NOT_HELD, which means your calling thread is missing a required privilege to run CreateProcessAsUser(). You don't need to, nor should you be, impersonating the user token in order to launch a new process in the user's desktop. You should be letting the thread use the service's credentials, not the user's credentials, when calling CreateProcessAsUser(). It will make sure the new process is run inside the user's account and desktop for you, so get rid of the call to ImpersonateLoggedOnUser() and see if CreateProcessAsUser() starts working.
Update: read the documentation:
Typically, the process that calls the CreateProcessAsUser function must have the SE_INCREASE_QUOTA_NAME privilege and may require the SE_ASSIGNPRIMARYTOKEN_NAME privilege if the token is not assignable. If this function fails with ERROR_PRIVILEGE_NOT_HELD (1314), use the CreateProcessWithLogonW function instead. CreateProcessWithLogonW requires no special privileges, but the specified user account must be allowed to log on interactively. Generally, it is best to use CreateProcessWithLogonW to create a process with alternate credentials.

On Vista and later, windows services run in session 0. Interactive users exist in session 1 and up. This means that windows services cannot show user interface and indeed cannot easily start processes in the interactive session.
Now, there are ways to launch interactive processes from a service. If you are dead set on launching an interactive process from your service, then that article tells you all your need to know. But such techniques are very tricky and absolutely not to be recommended. The recommendation is that you find a different way to communicate between your service and the interactive desktop.
The normal approach is to run a standard desktop app, perhaps started using HKLM\Software\Microsoft\Windows\CurrentVersion\Run. And use some form of IPC to communicate between the desktop app and the service.

You should be creating the service to do the background work, and the GUI application should only call the service. IE you always have a service running.
Consider using the DataSnap stuff for the back end. An MVC approach is not pure in Delphi like it is in some other languages. The controller goes wherever it is convenient. Datasets are mostly a compromise, and the only really fast way to do data is with DBexpress and a whack of components on the client to keep it all happy. But it works and it is worth learning.
Services cant have gui controls. No TForm descendents allowed. TService only. New project under Delphi Projects / Service Application. You get a project with a unit / form that is pretty much the same as a data module. ie, no visual controls allowed. Reasons are pretty obvious. You need to learn records, object design etc. to use services. Datasnap is your best bet for that. It does most of the hard work for you.
Dr. Bob has a pretty good book on it for XE2/3. If you are new to object oriented design you need to learn that thoroughly first.

Here is the code i use to do this kind of thing
procedure CreateNewProcess;
var
CmdLine: AnsiString;
ErrorCode: Cardinal;
ConnSessID: Cardinal;
Token: Cardinal;
App: AnsiString;
FProcessInfo: _PROCESS_INFORMATION;
FStartupInfo: _STARTUPINFOA;
begin
ZeroMemory(#FStartupInfo, SizeOf(FStartupInfo));
FStartupInfo.cb := SizeOf(FStartupInfo);
FStartupInfo.lpDesktop := nil;
ConnSessID := WTSGetActiveConsoleSessionId;
if WTSQueryUserToken(ConnSessID, Token) then
begin
if CreateProcessAsUser(Token, PAnsiChar(App), PAnsiChar(CmdLine),
nil, nil, false, 0, nil, nil, FStartupInfo, FProcessInfo) = False
then
begin
ErrorCode := GetLastError;
try
RaiseLastOSError(ErrorCode);
except on E: Exception do
EventLog.LogError(e.ClassName +': '+ e.Message);
end;
end;
end;
end;
Hope this helps
if you want the service to wait for the new process to terminate before continuing add this
if CreateProcessAsUser(Token, PAnsiChar(App), PAnsiChar(CmdLine),
nil, nil, false, 0, nil, nil, FStartupInfo, FProcessInfo) = False
then
begin
ErrorCode := GetLastError;
try
RaiseLastOSError(ErrorCode);
except on E: Exception do
EventLog.LogError(e.ClassName +': '+ e.Message);
end;
end
else
WaitForSingleObject(FProcessInfo.hProcess, INFINITE);
although and infinite wait is probably not advisable.

Related

How to find out if Windows installer is busy or not using delphi?

I have a Windows service that restarts windows in specific events but i have a problem with it. I don't want to restart windows when it is installing programs using windows installer.
So how i can fine out whether Windows installer is busy installing something or not.
any Delphi or Command line Function is acceptable.
Will you please help me ?
I found these 2 classess but I don't know how to use them.
Class1 class2
Based on this article, which is quite outdated I've tried to implement both suggested options. The second one worked for me on Windows 7 SP1. The principle is to query the MSIServer service status and check, if this service is running and that accepts the SERVICE_ACCEPT_STOP control code. Here's a function wrapper for that:
uses
WinSvc;
function IsWindowsInstallerBusy: Boolean;
var
Service: SC_HANDLE;
ServiceMgr: SC_HANDLE;
ServiceStatus: SERVICE_STATUS;
begin
Result := False;
ServiceMgr := OpenSCManager(nil, nil, SC_MANAGER_CONNECT);
if ServiceMgr <> 0 then
try
Service := OpenService(ServiceMgr, 'MSIServer', SERVICE_QUERY_STATUS);
if Service <> 0 then
try
if QueryServiceStatus(Service, ServiceStatus) then
begin
Result := (ServiceStatus.dwCurrentState = SERVICE_RUNNING) and
((ServiceStatus.dwControlsAccepted and SERVICE_ACCEPT_STOP) = 0);
end
else
raise Exception.CreateFmt('Cannot query service status. Code: %d',
[GetLastError]);
finally
CloseServiceHandle(Service);
end
else
raise Exception.CreateFmt('Cannot open service. Code: %d',
[GetLastError]);
finally
CloseServiceHandle(ServiceMgr);
end
else
raise Exception.CreateFmt('Cannot connect to the service control ' +
'manager. Code: %d', [GetLastError]);
end;
Windows Installer provides a mutex to tell you if an installation is in process. I would use this over the SCManager API calls because it is thread safe. Heath Stewart (MSFT MSI Expert) wrote about it here and his advice should be given higher credibility then that of the Windows Installer team.
I'm just guessing here because you did not provide enough information...
(Some details about the goal and circumstances would have been nice.)
1) If you wish to restart the windows only when a specific installer is not running, you should enumerate running processes and if you find the installer you want to be aware of simply don't restart your system.
2) If you wish for a more generalised solution you still have a choice: The .msi typed installers are using msiexec.exe. If you wish to be aware of an installer of this type, you can search the list of the running processes for that.
Resources:
About process enumeration read the MSDN documentation on the topic.
About doing it from delphi read this tutorial

How to not trigger exception when trying to write to HKLM on standard user?

I am attempting to write a value to the HKLM registry using TRegistry component in Delphi.
Since I am running on Windows 2000 as a standard user (or XP as a standard user, or Windows Vista as a standard user, or Windows 7 with a standard user), I fully expect that I will not be able to write to the HKEY_LOCAL_MACHINE portion of the registry:
reg := TRegistry.Create(KEY_WRITE);
try
reg.Access := KEY_WRITE; //sure, set it again, why not
reg.RootKey := HKEY_LOCAL_MACHINE;
if not reg.OpenKey('\Software\Microsoft\SQMClient', True) then
Exit;
reg.WriteString('MachineId', s);
finally
reg.Free;
end;
Unfortunately, the WriteString throws an ERegistryException:
Failed to set data for 'MachineId`
This is fully expected, which is why I'm trying to avoid the exception. I do not see any CanWriteString or TryWriteString in TRegistry.
How can I not trigger an exception when trying to write to HKLM?
Self-evident notes:
if the user actually is an administrator then the write should be able to succeed
wrapping the call to WriteString in a try-except:
reg := TRegistry.Create(KEY_WRITE);
try
reg.RootKey := HKEY_LOCAL_MACHINE;
if not reg.OpenKey('\Software\Microsoft\SQMClient', True) then
Exit;
try
reg.WriteString('MachineId', s);
except
on E:ERegistryException do
{nothing};
end;
finally
reg.Free;
end;
doesn't prevent the exception from being thrown in the first place.
Update: From RTL source:
KEY_WRITE = (STANDARD_RIGHTS_WRITE or
KEY_SET_VALUE or
KEY_CREATE_SUB_KEY) and not
SYNCHRONIZE;
from MSDN:
KEY_WRITE (0x20006)
Combines the STANDARD_RIGHTS_WRITE, KEY_SET_VALUE, and KEY_CREATE_SUB_KEY access rights.
You can't get TRegistry to behave the way you want. There are no TryXXX methods and there are not parameters that disable exceptions. You can be sure that this is so because the TRegistry methods do not provide any error or status codes.
You will have to write your own wrappers around the Win32 registry API.
As an aside, I agree with your opinion, expressed in the comments, that TRegistry is lacking in functionality here. We expect registry operations to fail, and so we should not have to catch exceptions to deal with that.
Use KEY_SET_VALUE instead of KEY_WRITE when opening the key, as KEY_WRITE includes other permissions in it. The fact that OpenKey() succeeds means that your standard user account has some of those permissions, so the key is allowed to be opened, but the key does not know exactly what you are going to do with it until you do it, so it cannot actually validate all of the permissions up front in case you dont use them. If you use just KEY_SET_VALUE instead (which is all you really need in your example), OpenKey() has a better chance of failing right away if your user account does not have any permissions to write data into the key. When it comes to accessing securable resources, always request just the minimum permissions you actually need.

How to use Lookupqueue?

I use Delphi 2007 and I try to find out how to ask Windows (XP, Server 2003 or 2008) if a named MSMQ queue is installed. I have found this but it is in C++ so it is not easy to use from Delphi. Example, I have an installed queue named '.\private$\nctsinqueue'. It works fine to use it by:
var
QueueInfo : IMSMQQueueInfo2;
begin
QueueInfo := CoMSMQQueueInfo.Create;
The problem is that in some installations of Windows where my application is installed this queue does not exists. It depend of the preferences if a queue is needed. So I want to ask Windows if a named queue is installed and in that case I can go on with the code above.
EDIT:
Tried this code
function Test: Boolean;
var
QueueInfo : IMSMQQueueInfo2;
begin
Result := True;
QueueInfo := CoMSMQQueueInfo.Create;
QueueInfo.PathName := '.\private$\nonexistingqueue';
FQueue := QueueInfo.Open(MQ_RECEIVE_ACCESS, MQ_DENY_NONE);
end;
And it raise an exception on the last line. I can of course have a try/except here and return False in that case but I don't like to have exceptionhandling for this. I want to ask WinApi or something if the queue exists. Queue.IsOpen that kobik suggest only says if an existing queue is opened. It must of course exist before it can be opened.
Edit2:
I take a more practical approach to this, so I solved it with ini-files for my application.
It tries to open only if the queue is present in the ini-file.
Disadvantage is of course that the ini-file must be in sync with the queues in the system, but that part is rather static.

Delphi - Gracefully Closing Created Process in Service. (using tprocess / createProcess)

I have a Windows Service written in Delphi which runs a number of programs.
On Stopping the service, I want to also close these programs. When the service was originally written, this worked fine, but I think I've updated the tProcess component and now - The subordinate programs are not being closed.
in tProcess - Here's the code which starts the new processes.
if CreateProcess( nil , PChar( FProcess.Command ) , nil , nil , False ,
NORMAL_PRIORITY_CLASS , nil , Directory ,
StartupInfo , ProcessInfo ) then
begin
if FProcess.Wait then
begin
WaitForSingleObject( ProcessInfo.hProcess , Infinite );
GetExitCodeProcess( ProcessInfo.hProcess , ExitCode );
if Assigned( FProcess.FOnFinished ) then
FProcess.FOnFinished( FProcess , ExitCode );
end;
CloseHandle( ProcessInfo.hProcess );
CloseHandle( ProcessInfo.hThread );
end;
Each of the executables called by this are Windows GUI Programs (With a close button at the top).
When I stop the service, I also want to stop (not kill) the programs I've started up via the createProcess procedure.
How would you do this?
I'd use TJvCreateProcess component of JVCL which wraps about any process related functionality of win32 in a graceful way. This answer comes from Dont-touch-winapi-unless-really-required department :-)
You want to enumerate open windows that match your launched ProcessId and tell those windows to close. Here's some sample code for that:
uses Windows;
interface
function MyTerminateAppEnum(hHwnd:HWND; dwData:LPARAM):Boolean; stdcall;
implementation
function MyTerminateAppEnum(hHwnd:HWND; dwData:LPARAM):Boolean;
var
vID:DWORD;
begin
GetWindowThreadProcessID(hHwnd, #vID);
if vID = dwData then
begin
PostMessage(hHwnd, WM_CLOSE, 0, 0); //tell window to close gracefully
Result := False; //can stop enumerating
end
else
begin
Result := TRUE; //keep enumerating until you find your id
end;
end;
Then you'll want to utilize this in your code when you want to shut down the launched applications:
Procedure TerminateMe(YourSavedProcessInfo:TProcessInformation);
var
vExitCode:UINT;
begin
GetExitCodeProcess(YourSavedProcessInfo.hProcess, vExitCode);
if (vExitCode = STILL_ACTIVE) then //launched app still running..
begin
//tell it to close
EnumWindows(#MyTerminateAppEnum, YourSavedProcessInfo.dwProcessId);
if WaitForSingleObject(YourSavedProcessInfo.hProcess, TIMEOUT_VAL) <> WAIT_OBJECT_0 then
begin
if not TerminateProcess(YourSavedProcessInfo.hProcess, 0) then //didn't close, try to terminate
begin
//uh-oh Didn't close, didn't terminate..
end;
end;
end;
CloseHandle(YourSavedProcessInfo.hProcess);
CloseHandle(YourSavedProcessInfo.hThread);
end;
The only generic way to stop a process is to use TerminateProcess. But that's as far from graceful as you can get. To gracefully close a process, you need to tell the process that you'd like it to stop, and then hope it obeys. There is no way to do that in general, though.
For a GUI program, the usual way to tell it that you want it to stop running is to close its main window. There's no formal idea of "main window," though. A program can have zero or more windows, and there's no way to know externally which one you're supposed to close in order to make the program stop working.
You could use EnumWindows to cycle through all the windows and select the ones that belong to your process. (They'd be the ones for which GetWindowThreadProcessId gives the same process ID that CreateProcess gave you.)
Closing a window might not be enough. The program might display a dialog box (prompting for confirmation, or asking to save changes, etc.). You would need to know in advance how to dismiss that dialog box.
Non-GUI programs can have similar problems. It might be enough to simulate a Ctrl+C keystroke. It might catch and handle that keystroke differently, though. It might have a menu system that expects you to type "Q" to quit the program.
In short, you cannot gracefully close a program unless you know in advance how that program expects to be closed.

How can I tell if another instance of my program is already running?

How do i tell if one instance of my program is running?
I thought I could do this with a data file but it would just be messy :(
I want to do this as I only want 1 instance to ever be open at one point.
As Jon first suggested, you can try creating a mutex. Call CreateMutex. If you get a non-null handle back, then call GetLastError. It will tell you whether you were the one who created the mutex or whether the mutex was already open before (Error_Already_Exists). Note that it is not necessary to acquire ownership of the mutex. The mutex is not being used for mutual exclusion. It's being used because it is a named kernel object. An event or semaphore could work, too.
The mutex technique gives you a Boolean answer: Yes, there is another instance, or no, there is not.
You frequently want to know more than just that. For instance, you might want to know the handle of the other instance's main window so you can tell it to come to the foreground in place of your other instance. That's where a memory-mapped file can come in handy; it can hold information about the first instance so later instances can refer to it.
Be careful when choosing the name of the mutex. Read the documentation carefully, and keep in mind that some characters (such as backslash) are not allowed in some OS versions, but are required for certain features in other OS versions.
Also remember the problem of other users. If your program could be run via remote desktop or fast user switching, then there could be other users already running your program, and you might not really want to restrict the current user from running your program. In that case, don't use a global name. If you do want to restrict access for all users, then make sure the mutex object's security attributes are such that everyone will be able to open a handle to it. Using a null pointer for the lpSecurityAttributes parameter is not sufficient for that; the "default security descriptor" that MSDN mentions gives full access to the current user and no access to others.
You're allowed to edit the DPR file of your program. That's usually a good place to do this kind of thing. If you wait until the OnCreate event of one of your forms, then your program already has a bit of momentum toward running normally, so it's clumsy to try to terminate the program at that point. Better to terminate before too much UI work has been done. For example:
var
mutex: THandle;
mutexName: string;
begin
mutexName := ConstructMutexName();
mutex := CreateMutex(nil, False, PChar(mutexName));
if mutex = 0 then
RaiseLastOSError; // Couldn't open handle at all.
if GetLastError = Error_Already_Exists then begin
// We are not the first instance.
SendDataToPreviousInstance(...);
exit;
end;
// We are the first instance.
// Do NOT close the mutex handle here. It must
// remain open for the duration of your program,
// or else later instances won't be able to
// detect this instance.
Application.Initialize;
Application.CreateForm(...);
Application.Run;
end.
There's a question of when to close the mutex handle. You don't have to close it. When your process finally terminates (even if it crashes), the OS will automatically close any outstanding handles, and when there are no more handles open, the mutex object will be destroyed (thus allowing another instance of your program to start and consider itself to be the first instance).
But you might want to close the handle anyway. Suppose you chose to implement the SendDataToPreviousInstance function I mentioned in the code. If you want to get fancy, then you could account for the case that the previous instance is already shutting down and is unable to accept new data. Then you won't really want to close the second instance. The first instance could close the mutex handle as soon as it knows it's shutting down, in effect becoming a "lame duck" instance. The second instance will try to create the mutex handle, succeed, and consider itself the real first instance. The previous instance will close uninterrupted. Use CloseHandle to close the mutex; call it from your main form's OnClose event handler, or wherever else you call Application.Terminate, for example.
You can create a Semaphore and stop execution (put the code into your *.dpr file) and bring you running application to the screen.
var
Semafor: THandle;
begin
{ Don't start twice ... if already running bring this instance to front }
Semafor := CreateSemaphore(nil, 0, 1, 'MY_APPLICATION_IS_RUNNING');
if ((Semafor <> 0) and { application is already running }
(GetLastError = ERROR_ALREADY_EXISTS)) then
begin
RestoreWindow('TMyApplication');
CloseHandle(Semafor);
Halt;
end;
Application.CreateForm(....);
Application.Initialize;
Application.Run;
CloseHandle(Semafor);
end;
EDIT (added the RestoreWindow method):
The aFormName is the name of your main form class in your application.
procedure RestoreWindow(aFormName: string);
var
Wnd,
App: HWND;
begin
Wnd := FindWindow(PChar(aFormName), nil);
if (Wnd <> 0) then
begin { Set Window to foreground }
App := GetWindowLong(Wnd, GWL_HWNDPARENT);
if IsIconic(App) then
ShowWindow(App, SW_RESTORE);
SetForegroundwindow(App);
end;
end;
The all-mighty JVCL has a component for this purpose. See "TJvAppInstances".
The normal solution is to create a named, system-wide mutex.
If you manage to create it, you're the one running application.
If you don't, you know there's a different one.
EDIT:
I haven't provided code as I don't know Delphi. I can provide C# code if that would be helpful though.
You create a system mutex.
I don't have Delphi code, but here's C++ code:
HANDLE Mutex;
const char MutexName[] = "MyUniqueProgramName";
Mutex = OpenMutex(MUTEX_ALL_ACCESS, false, MutexName);
if (Mutex)
throw Exception("Program is already running.");
else
Mutex = CreateMutex(NULL, true, MutexName);
I'd like to add one point to the excellent answer by Rob Kennedy (apart from the fact that it would be best to make a function out of his code instead of copying everything into the DPR file. You only need two parameters, the name of the mutex, and a boolean whether the mutext should be per-user or system-wide).
The answer does not give much consideration to the naming of the mutex. If you expect your program to be installed via Inno Setup (and maybe other setup tools too) you should choose the name carefully, as the mutex can be used to have the setup program check whether the application is currently running, and alert the user that they should close all instances of the application. If you choose to allow one instance of the program per user you may need to create a second system-wide mutex too, as the setup may need to have no running instances of the application at all in order to be able to replace files. The name that is to be used for synchronization with an InnoSetup installer must be hard-coded.
I would say that there are several different strategies that you can employ. But the easiest one (and not platform specific) is the one you yourself suggested, namely to, at the start of the program check to see if there is a lock file created in a set, specific location. If this lock file exists, then another instance is already running, if it doesn't exist, then there is not another instance running. When your program exits, you delete the lock file.
However, employing this strategy you have another problem, what happens if your program crashes? The lock file still remains, and this specific case need to be handled.
Another strategy is the system-wide mutex solution, where you register your presence within the operating system (or it's also plausible that this is done automagically). When a second instance then tries to start, it checks if there's already a process active with a specific ID. If it already exists, the second process chooses not to start, and optionally brings the first process' window in focus (if the process in question owns a window that is).
However, this strategy is platform specific, and the implementation will differ from platform to platform.
You can simply use FindWindow windows api function. In delphi class name of the window is the same as class name, you can redefine class name by overriding CreateParams function. To check if window exists add code before main window is created , before Application.Initialize;
Program test
var
handle :HWND;
begin
handle := FindWindow('TMySuperApp', nil);
if IsWindow(handle) then
begin
//app is running
exit;
end.
Application.Initialize;
Application.CreateForm(TMySuperApp, SuperApp);
Application.Run;
end;
Controlling the number of application instances:
http://delphi.about.com/od/windowsshellapi/l/aa100703a.htm
If You want to stop execution your app more then once in the same time (put the code into *.dpr file of the project).
will show a message after second app will be running and stop it instantly .
Forms,
Unit1 in 'Unit1.pas' {Form1},
// add this units ....
TlHelp32,SysUtils,Windows,Dialogs;
{$R *.res}
function ProcessCount(const ExeName: String): Integer;
var
ContinueLoop: BOOL;
FSnapshotHandle: THandle;
FProcessEntry32: TProcessEntry32;
begin
FSnapshotHandle:= CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
FProcessEntry32.dwSize:= SizeOf(FProcessEntry32);
ContinueLoop:= Process32First(FSnapshotHandle, FProcessEntry32);
Result:= 0;
while Integer(ContinueLoop) <> 0 do begin
if ((UpperCase(ExtractFileName(FProcessEntry32.szExeFile)) =
UpperCase(ExeName)) or (UpperCase(FProcessEntry32.szExeFile) =
UpperCase(ExeName))) then Inc(Result);
ContinueLoop:= Process32Next(FSnapshotHandle, FProcessEntry32);
end;
CloseHandle(FSnapshotHandle);
end;
begin
if ProcessCount(ExtractFileName(Application.ExeName)) > 1 then begin
MessageDlg('Application is already running!', mtError, [mbOK], 0);
Application.Terminate;
end else begin
Application.Initialize;
Application.CreateForm(TForm1, Form1);
Application.Run;
end;
end.
See this unit (using CreateMutex): UiApp
Additionally at this page, you can read the advantages and disadvantages for to this work with differents methods (mutex, FindWindows,...).
This unit have the solution to activate the previos instance of the application when this is detected.
Regards and excuse-me for my bad english.
Neftalí -Germán Estévez-
In the past, I've used a socket to prevent multiple instances from running at the same time. If the socket is in use, don't continue the program, if it's available let everything run as normal.

Resources