Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
CustomAction.cs
1using System;
2using System.Collections.Generic;
3using System.Diagnostics;
4using System.IO;
5using System.Reflection;
6using System.ServiceProcess;
7using System.Text;
8using System.Threading;
9using System.Xml;
10using Microsoft.Deployment.WindowsInstaller;
11using Waher.Content;
14using static System.Environment;
15
17{
18 public partial class CustomActions
19 {
20 [CustomAction]
21 public static ActionResult CreateEventSource(Session Session)
22 {
23 Session.Log("Checking event sources.");
24
25 try
26 {
27 if (!EventLog.Exists("IoTGateway") || !EventLog.SourceExists("IoTGateway"))
28 {
29 Session.Log("Creating event source.");
30 EventLog.CreateEventSource(new EventSourceCreationData("IoTGateway", "IoTGateway"));
31 Session.Log("Event source created.");
32 }
33
34 return ActionResult.Success;
35 }
36 catch (Exception ex)
37 {
38 Session.Log("Unable to create event source. Error reported: " + ex.Message);
39 return ActionResult.Failure;
40 }
41 }
42
43 [CustomAction]
44 public static ActionResult DeleteEventSource(Session Session)
45 {
46 Session.Log("Checking event sources.");
47
48 if (EventLog.Exists("IoTGateway"))
49 {
50 try
51 {
52 Session.Log("Deleting event log.");
53 EventLog.Delete("IoTGateway");
54 Session.Log("Event log deleted.");
55 }
56 catch (Exception ex)
57 {
58 Session.Log("Unable to delete event log. Error reported: " + ex.Message);
59 }
60 }
61
62 if (EventLog.SourceExists("IoTGateway"))
63 {
64 try
65 {
66 Session.Log("Deleting event source.");
67 EventLog.DeleteEventSource("IoTGateway");
68 Session.Log("Event source deleted.");
69 }
70 catch (Exception ex)
71 {
72 Session.Log("Unable to delete event source. Error reported: " + ex.Message);
73 // Ignore.
74 }
75 }
76
77 return ActionResult.Success;
78 }
79
80 private static void Log(Session Session, string Msg)
81 {
82 Session.Log(Msg);
83 Session["Log"] = Msg;
84 }
85
86 public static string AppDataFolder
87 {
88 get
89 {
90 string Result = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData);
91
92 if (!Result.EndsWith(new string(Path.DirectorySeparatorChar, 1)))
93 Result += Path.DirectorySeparatorChar;
94
95 Result += "IoT Gateway" + Path.DirectorySeparatorChar;
96 if (!Directory.Exists(Result))
97 Directory.CreateDirectory(Result);
98 //{
99 // try
100 // {
101 // Directory.CreateDirectory(Result);
102 // }
103 // catch (Exception ex)
104 // {
105 // if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
106 // {
107 // Result = Result.Replace("/usr/share", "/usr/local/share");
108 // Directory.CreateDirectory(Result);
109 // }
110 // else
111 // ExceptionDispatchInfo.Capture(ex).Throw();
112 // }
113 //}
114
115 return Result;
116 }
117 }
118
119 [CustomAction]
120 public static ActionResult InstallAndStartService(Session Session)
121 {
122 Session.Log("Installing service.");
123 try
124 {
125 string DisplayName = Session["SERVICEDISPLAYNAME"];
126 string Description = Session["SERVICEDESCRIPTION"];
127 string InstallDir = Session["INSTALLDIR"];
128
129 if (!InstallDir.EndsWith(new string(Path.DirectorySeparatorChar, 1)))
130 InstallDir += Path.DirectorySeparatorChar;
131
132 Session.Log("Service Display Name: " + DisplayName);
133 Session.Log("Service Description: " + Description);
134 Session.Log("Working folder: " + InstallDir);
135
136 StringBuilder sb = new StringBuilder();
137
138 sb.Append("-install -displayname \"");
139 sb.Append(DisplayName);
140 sb.Append("\" -description \"");
141 sb.Append(Description);
142 sb.Append("\" -start AutoStart -immediate");
143
144 ProcessStartInfo ProcessInformation = new ProcessStartInfo()
145 {
146 FileName = InstallDir + "Waher.IotGateway.Svc.exe",
147 Arguments = sb.ToString(),
148 UseShellExecute = false,
149 RedirectStandardError = true,
150 RedirectStandardOutput = true,
151 WorkingDirectory = InstallDir,
152 CreateNoWindow = true,
153 WindowStyle = ProcessWindowStyle.Hidden
154 };
155
156 using (Process P = new Process())
157 {
158 bool Error = false;
159
160 P.ErrorDataReceived += (Sender, e) =>
161 {
162 Error = true;
163 Session.Log("ERROR: " + e.Data);
164 };
165
166 P.Exited += (Sender, e) =>
167 {
168 Session.Log("Process exited.");
169 };
170
171 P.OutputDataReceived += (Sender, e) =>
172 {
173 Session.Log(e.Data);
174 };
175
176 P.StartInfo = ProcessInformation;
177 P.Start();
178
179 if (!P.WaitForExit(60000) || Error)
180 throw new Exception("Timeout. Service did not install properly.");
181 else
182 {
183 if (!P.StandardError.EndOfStream)
184 Session.Log(P.StandardError.ReadToEnd());
185
186 if (!P.StandardOutput.EndOfStream)
187 Session.Log(P.StandardOutput.ReadToEnd());
188
189 if (P.ExitCode != 0)
190 throw new Exception("Installation failed. Exit code: " + P.ExitCode.ToString());
191 }
192 }
193
194 Session.Log("Service installed and started.");
195
196 return WaitAllModulesStarted(Session);
197 }
198 catch (Exception ex)
199 {
200 Session.Log("Unable to install service. Error reported: " + ex.Message);
201 return ActionResult.Failure;
202 }
203 }
204
205 [CustomAction]
206 public static ActionResult UninstallService(Session Session)
207 {
208 Session.Log("Uninstalling service.");
209 try
210 {
211 string InstallDir = Session["INSTALLDIR"];
212
213 if (!InstallDir.EndsWith(new string(Path.DirectorySeparatorChar, 1)))
214 InstallDir += Path.DirectorySeparatorChar;
215
216 Session.Log("Working folder: " + InstallDir);
217
218 ProcessStartInfo ProcessInformation = new ProcessStartInfo()
219 {
220 FileName = InstallDir + "Waher.IotGateway.Svc.exe",
221 Arguments = "-uninstall",
222 UseShellExecute = false,
223 RedirectStandardError = true,
224 RedirectStandardOutput = true,
225 WorkingDirectory = InstallDir,
226 CreateNoWindow = true,
227 WindowStyle = ProcessWindowStyle.Hidden
228 };
229
230 using (Process P = new Process())
231 {
232 bool Error = false;
233
234 P.ErrorDataReceived += (Sender, e) =>
235 {
236 Error = true;
237 Session.Log("ERROR: " + e.Data);
238 };
239
240 P.Exited += (Sender, e) =>
241 {
242 Session.Log("Process exited.");
243 };
244
245 P.OutputDataReceived += (Sender, e) =>
246 {
247 Session.Log(e.Data);
248 };
249
250 P.StartInfo = ProcessInformation;
251 P.Start();
252
253 if (!P.WaitForExit(60000) || Error)
254 Session.Log("Timeout. Service did not uninstall properly.");
255 else
256 {
257 if (!P.StandardError.EndOfStream)
258 Session.Log(P.StandardError.ReadToEnd());
259
260 if (!P.StandardOutput.EndOfStream)
261 Session.Log(P.StandardOutput.ReadToEnd());
262
263 if (P.ExitCode != 0)
264 Session.Log("Uninstallation failed. Exit code: " + P.ExitCode.ToString());
265 else
266 {
267 Session.Log("Service uninstalled.");
268 return ActionResult.Success;
269 }
270 }
271 }
272 }
273 catch (Exception ex)
274 {
275 Session.Log("Unable to uninstall service. Error reported: " + ex.Message);
276 }
277
278 return UninstallService2(Session);
279 }
280
281 public static ActionResult UninstallService2(Session Session)
282 {
283 Session.Log("Uninstalling service (method 2).");
284 try
285 {
286 string InstallDir = Session["INSTALLDIR"];
287
288 if (!InstallDir.EndsWith(new string(Path.DirectorySeparatorChar, 1)))
289 InstallDir += Path.DirectorySeparatorChar;
290
291 Session.Log("Working folder: " + InstallDir);
292
293 ProcessStartInfo ProcessInformation = new ProcessStartInfo()
294 {
295 FileName = "sc.exe",
296 Arguments = "delete \"IoT Gateway Service\"",
297 UseShellExecute = false,
298 RedirectStandardError = true,
299 RedirectStandardOutput = true,
300 WorkingDirectory = InstallDir,
301 CreateNoWindow = true,
302 WindowStyle = ProcessWindowStyle.Hidden
303 };
304
305 using (Process P = new Process())
306 {
307 bool Error = false;
308
309 P.ErrorDataReceived += (Sender, e) =>
310 {
311 Error = true;
312 Session.Log("ERROR: " + e.Data);
313 };
314
315 P.Exited += (Sender, e) =>
316 {
317 Session.Log("Process exited.");
318 };
319
320 P.OutputDataReceived += (Sender, e) =>
321 {
322 Session.Log(e.Data);
323 };
324
325 P.StartInfo = ProcessInformation;
326 P.Start();
327
328 if (!P.WaitForExit(60000) || Error)
329 Session.Log("Timeout. Service did not uninstall properly.");
330 else
331 {
332 if (!P.StandardError.EndOfStream)
333 Session.Log(P.StandardError.ReadToEnd());
334
335 if (!P.StandardOutput.EndOfStream)
336 Session.Log(P.StandardOutput.ReadToEnd());
337
338 if (P.ExitCode != 0)
339 Session.Log("Uninstallation failed. Exit code: " + P.ExitCode.ToString());
340 else
341 Session.Log("Service uninstalled.");
342 }
343 }
344 }
345 catch (Exception ex)
346 {
347 Session.Log("Unable to uninstall service. Error reported: " + ex.Message);
348
349 }
350
351 return ActionResult.Success;
352 }
353
354 [CustomAction]
355 public static ActionResult StartService(Session Session)
356 {
357 Session.Log("Starting service.");
358 try
359 {
360 string InstallDir = Session["INSTALLDIR"];
361
362 ProcessStartInfo ProcessInformation = new ProcessStartInfo()
363 {
364 FileName = "net",
365 Arguments = "start \"IoT Gateway Service\"",
366 UseShellExecute = false,
367 RedirectStandardError = true,
368 RedirectStandardOutput = true,
369 WorkingDirectory = InstallDir,
370 CreateNoWindow = true,
371 WindowStyle = ProcessWindowStyle.Hidden
372 };
373
374 using (Process P = new Process())
375 {
376 bool Error = false;
377
378 P.ErrorDataReceived += (Sender, e) =>
379 {
380 Error = true;
381 Session.Log("ERROR: " + e.Data);
382 };
383
384 P.Exited += (Sender, e) =>
385 {
386 Session.Log("Process exited.");
387 };
388
389 P.OutputDataReceived += (Sender, e) =>
390 {
391 Session.Log(e.Data);
392 };
393
394 P.StartInfo = ProcessInformation;
395 P.Start();
396
397 if (!P.WaitForExit(60000) || Error)
398 throw new Exception("Timeout. Service did not start properly.");
399 else
400 {
401 if (!P.StandardError.EndOfStream)
402 Session.Log(P.StandardError.ReadToEnd());
403
404 if (!P.StandardOutput.EndOfStream)
405 Session.Log(P.StandardOutput.ReadToEnd());
406
407 if (P.ExitCode != 0)
408 throw new Exception("Service start failed. Exit code: " + P.ExitCode.ToString());
409 }
410 }
411
412 Session.Log("Service started.");
413
414 return WaitAllModulesStarted(Session);
415 }
416 catch (Exception ex)
417 {
418 Session.Log("Unable to start service. Error reported: " + ex.Message);
419 //return ActionResult.Failure;
420 return ActionResult.Success;
421 }
422 }
423
424 [CustomAction]
425 public static ActionResult StopService(Session Session)
426 {
427 Session.Log("Stopping service.");
428 try
429 {
430 string InstallDir = Session["INSTALLDIR"];
431
432 ProcessStartInfo ProcessInformation = new ProcessStartInfo()
433 {
434 FileName = "net",
435 Arguments = "stop \"IoT Gateway Service\"",
436 UseShellExecute = false,
437 RedirectStandardError = true,
438 RedirectStandardOutput = true,
439 WorkingDirectory = InstallDir,
440 CreateNoWindow = true,
441 WindowStyle = ProcessWindowStyle.Hidden
442 };
443
444 using (Process P = new Process())
445 {
446 bool Error = false;
447
448 P.ErrorDataReceived += (Sender, e) =>
449 {
450 Error = true;
451 Session.Log("ERROR: " + e.Data);
452 };
453
454 P.Exited += (Sender, e) =>
455 {
456 Session.Log("Process exited.");
457 };
458
459 P.OutputDataReceived += (Sender, e) =>
460 {
461 Session.Log(e.Data);
462 };
463
464 P.StartInfo = ProcessInformation;
465 P.Start();
466
467 if (!P.WaitForExit(60000) || Error)
468 Session.Log("Timeout. Service did not stop properly.");
469 else
470 {
471 if (!P.StandardError.EndOfStream)
472 Session.Log(P.StandardError.ReadToEnd());
473
474 if (!P.StandardOutput.EndOfStream)
475 Session.Log(P.StandardOutput.ReadToEnd());
476
477 if (P.ExitCode != 0)
478 Session.Log("Stopping service failed. Exit code: " + P.ExitCode.ToString());
479 else
480 {
481 DateTime Started = DateTime.Now;
482 bool Stopped;
483
484 Session.Log("Service stop request successful. Checking process has stopped");
485
486 do
487 {
488 using (Mutex RunningServer = new Mutex(false, "Waher.IoTGateway.Running"))
489 {
490 Stopped = RunningServer.WaitOne(1000);
491
492 if (Stopped)
493 RunningServer.ReleaseMutex();
494 }
495 }
496 while (!Stopped && (DateTime.Now - Started).TotalSeconds < 30);
497
498 if (Stopped)
499 Session.Log("Service stopped.");
500 else
501 throw new Exception("Service stop procedure seems to take time. Cancelling wait.");
502 }
503 }
504 }
505 }
506 catch (Exception ex)
507 {
508 Session.Log("Unable to stop service. Error reported: " + ex.Message);
509 }
510
511 return ActionResult.Success;
512 }
513
514 /*[CustomAction]
515 public static ActionResult DisableHttpService(Session Session)
516 {
517 Session.Log("Stopping HTTP service.");
518 try
519 {
520 ProcessStartInfo ProcessInformation = new ProcessStartInfo();
521 ProcessInformation.FileName = "net";
522 ProcessInformation.Arguments = "stop http /y";
523 ProcessInformation.UseShellExecute = false;
524 ProcessInformation.RedirectStandardError = true;
525 ProcessInformation.RedirectStandardOutput = true;
526 ProcessInformation.CreateNoWindow = true;
527 ProcessInformation.WindowStyle = ProcessWindowStyle.Hidden;
528
529 Process P = new Process();
530 bool Error = false;
531
532 P.ErrorDataReceived += (Sender, e) =>
533 {
534 Error = true;
535 Session.Log("ERROR: " + e.Data);
536 };
537
538 P.Exited += (Sender, e) =>
539 {
540 Session.Log("Process exited.");
541 };
542
543 P.OutputDataReceived += (Sender, e) =>
544 {
545 Session.Log(e.Data);
546 };
547
548 P.StartInfo = ProcessInformation;
549 P.Start();
550
551 if (!P.WaitForExit(60000) || Error)
552 Session.Log("Timeout. HTTP service did not stop properly.");
553 else
554 {
555 if (!P.StandardError.EndOfStream)
556 Session.Log(P.StandardError.ReadToEnd());
557
558 if (!P.StandardOutput.EndOfStream)
559 Session.Log(P.StandardOutput.ReadToEnd());
560
561 if (P.ExitCode != 0)
562 Session.Log("Stopping http service failed. Exit code: " + P.ExitCode.ToString());
563 else
564 Session.Log("Service stopped.");
565 }
566
567 Session.Log("Disabling http service.");
568
569 ProcessInformation = new ProcessStartInfo();
570 ProcessInformation.FileName = "sc";
571 ProcessInformation.Arguments = "config http start=disabled";
572 ProcessInformation.UseShellExecute = false;
573 ProcessInformation.RedirectStandardError = true;
574 ProcessInformation.RedirectStandardOutput = true;
575 ProcessInformation.CreateNoWindow = true;
576 ProcessInformation.WindowStyle = ProcessWindowStyle.Hidden;
577
578 P = new Process();
579 Error = false;
580
581 P.ErrorDataReceived += (Sender, e) =>
582 {
583 Error = true;
584 Session.Log("ERROR: " + e.Data);
585 };
586
587 P.Exited += (Sender, e) =>
588 {
589 Session.Log("Process exited.");
590 };
591
592 P.OutputDataReceived += (Sender, e) =>
593 {
594 Session.Log(e.Data);
595 };
596
597 P.StartInfo = ProcessInformation;
598 P.Start();
599
600 if (!P.WaitForExit(60000) || Error)
601 Session.Log("Timeout. HTTP service was not disabled properly.");
602 else
603 {
604 if (!P.StandardError.EndOfStream)
605 Session.Log(P.StandardError.ReadToEnd());
606
607 if (!P.StandardOutput.EndOfStream)
608 Session.Log(P.StandardOutput.ReadToEnd());
609
610 if (P.ExitCode != 0)
611 Session.Log("Disabling http service failed. Exit code: " + P.ExitCode.ToString());
612 else
613 Session.Log("Service disabled.");
614 }
615 }
616 catch (Exception ex)
617 {
618 Session.Log("Unable to disable http service. Error reported: " + ex.Message);
619 }
620
621 return ActionResult.Success;
622 }*/
623
624 [CustomAction]
625 public static ActionResult OpenLocalhost(Session Session)
626 {
627 Session.Log("Starting browser.");
628 try
629 {
630 Thread.Sleep(5000); // Give process some time.
631
632 string StartPage = Session["STARTPAGE"];
633 if (StartPage == "unset")
634 StartPage = string.Empty;
635
636 string Port = string.Empty;
637 string Protocol = "http";
638
639 try
640 {
641 string s = File.ReadAllText(AppDataFolder + "Ports.txt");
642 string[] Rows = s.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
643 SortedDictionary<int, bool> Ports = new SortedDictionary<int, bool>();
644
645 foreach (string Row in Rows)
646 {
647 if (int.TryParse(Row, out int i))
648 Ports[i] = true;
649 }
650
651 if (!Ports.ContainsKey(80))
652 {
653 if (Ports.ContainsKey(8080))
654 Port = ":8080";
655 else if (Ports.ContainsKey(8081))
656 Port = ":8081";
657 else if (Ports.ContainsKey(8082))
658 Port = ":8082";
659 else if (Ports.ContainsKey(443))
660 Protocol = "https";
661 else if (Ports.ContainsKey(8088))
662 {
663 Protocol = "https";
664 Port = ":8088";
665 }
666 }
667 }
668 catch (Exception ex)
669 {
670 Session.Log("Unable to get opened ports. Error reported: " + ex.Message);
671 }
672
673 StartPage = Protocol + "://localhost" + Port + "/" + StartPage;
674 Session.Log("Start Page: " + StartPage);
675
676 Process.Start(StartPage);
677 Session.Log("Browser started.");
678 }
679 catch (Exception ex)
680 {
681 Session.Log("Unable to start browser. Error reported: " + ex.Message);
682 }
683
684 return ActionResult.Success;
685 }
686
687 public static ActionResult WaitAllModulesStarted(Session Session)
688 {
689 Session.Log("Waiting for all modules to start.");
690 try
691 {
692 DateTime Start = DateTime.Now;
693 bool Running;
694
695 Session.Log("Waiting for service to start.");
696
697 Thread.Sleep(5000);
698
699 do
700 {
701 using (Mutex RunningServer = new Mutex(false, "Waher.IoTGateway.Running"))
702 {
703 Running = !RunningServer.WaitOne(1000);
704
705 if (!Running)
706 RunningServer.ReleaseMutex();
707 }
708
709 if (!Running)
710 Thread.Sleep(1000);
711 }
712 while (!Running && (DateTime.Now - Start).TotalSeconds < 30);
713
714 if (!Running)
715 {
716 Session.Log("Could not detect a start of service. Cancelling wait and continuing.");
717 return ActionResult.Success;
718 }
719
720 Session.Log("Waiting for service startup procedure to complete.");
721
722 using (Mutex StartingServer = new Mutex(false, "Waher.IoTGateway.Starting"))
723 {
724 if (StartingServer.WaitOne(120000))
725 {
726 StartingServer.ReleaseMutex();
727 Session.Log("All modules started.");
728 }
729 else
730 Session.Log("Modules takes too long to start. Cancelling wait and continuing.");
731 }
732 }
733 catch (Exception ex)
734 {
735 Session.Log("Unable to wait for all modules to start. The following error was reported: " + ex.Message);
736 }
737
738 return ActionResult.Success;
739 }
740
741 [CustomAction]
742 public static ActionResult BeforeUninstallEvent(Session Session)
743 {
744 Session.Log("Sending BeforeUninstall event.");
745 try
746 {
747 using (ServiceController ServiceController = new ServiceController("IoT Gateway Service"))
748 {
749 ServiceController.ExecuteCommand(128);
750 }
751 }
752 catch (Exception ex)
753 {
754 Session.Log("Unable to send event. The following error was reported: " + ex.Message);
755 }
756
757 return ActionResult.Success;
758 }
759
760 [CustomAction]
761 public static ActionResult InstallManifest(Session Session)
762 {
763 string ManifestFile = Path.Combine(Session["INSTALLDIR"], Session["ManifestFile"]);
764 string ServerApplication = Path.Combine(Session["INSTALLDIR"], "Waher.IoTGateway.Svc.dll");
765 string ProgramDataFolder = Session["APPDATADIR"];
766
767 Session.Log("Installing module: " + ManifestFile);
768 Session.Log("Server application: " + ServerApplication);
769 Session.Log("Program data folder: " + ProgramDataFolder);
770
771 try
772 {
773 Install(Session, ManifestFile, ServerApplication, ProgramDataFolder);
774 return ActionResult.Success;
775 }
776 catch (Exception ex)
777 {
778 Session.Log(ex.Message);
779 return ActionResult.Failure;
780 }
781 }
782
783 [CustomAction]
784 public static ActionResult StopServiceAndUninstallManifest(Session Session)
785 {
786 ActionResult Result = StopService(Session);
787 if (Result != ActionResult.Success)
788 return Result;
789
790 return UninstallManifest(Session);
791 }
792
793 [CustomAction]
794 public static ActionResult UninstallManifest(Session Session)
795 {
796 string ManifestFile = Path.Combine(Session["INSTALLDIR"], Session["ManifestFile"]);
797 string ServerApplication = Path.Combine(Session["INSTALLDIR"], "Waher.IoTGateway.Svc.dll");
798 string ProgramDataFolder = Session["APPDATADIR"];
799
800 Session.Log("Uninstalling module: " + ManifestFile);
801 Session.Log("Server application: " + ServerApplication);
802 Session.Log("Program data folder: " + ProgramDataFolder);
803
804 try
805 {
806 Uninstall(Session, ManifestFile, ServerApplication, ProgramDataFolder, true);
807 }
808 catch (Exception ex)
809 {
810 Session.Log(ex.Message);
811 }
812
813 return ActionResult.Success;
814 }
815
816 #region From Waher.Utility.Install
817
818 private static AssemblyName GetAssemblyName(string ServerApplication)
819 {
820 if (ServerApplication.EndsWith(".exe", StringComparison.CurrentCultureIgnoreCase))
821 ServerApplication = ServerApplication.Substring(0, ServerApplication.Length - 4) + ".dll";
822
823 return AssemblyName.GetAssemblyName(ServerApplication);
824 }
825
826 private static void Install(Session Session, string ManifestFile, string ServerApplication, string ProgramDataFolder)
827 {
828 // Same code as for custom action InstallManifest in Waher.IoTGateway.Installers, except:
829 // * Content files can already be installed in the corresponding application data folder.
830 // * Content-Only installations not supported.
831 // * No Schema validation of manifest files performed.
832 // * Logging is done to session, instead of event log.
833
834 if (string.IsNullOrEmpty(ManifestFile))
835 throw new Exception("Missing manifest file.");
836
837 if (string.IsNullOrEmpty(ProgramDataFolder))
838 {
839 ProgramDataFolder = Path.Combine(Environment.GetFolderPath(SpecialFolder.CommonApplicationData), "IoT Gateway");
840 //if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX) && !Directory.Exists(ProgramDataFolder))
841 // ProgramDataFolder = ProgramDataFolder.Replace("/usr/share", "/usr/local/share");
842
843 Session.Log("Using default program data folder: " + ProgramDataFolder);
844 }
845
846 if (string.IsNullOrEmpty(ServerApplication))
847 throw new Exception("Missing server application.");
848
849 if (!File.Exists(ServerApplication))
850 throw new Exception("Server application not found: " + ServerApplication);
851
852 Session.Log("Getting assembly name of server.");
853 AssemblyName ServerName = GetAssemblyName(ServerApplication);
854 Session.Log("Server assembly name: " + ServerName.ToString());
855
856 string DepsJsonFileName;
857
858 int i = ServerApplication.LastIndexOf('.');
859 if (i < 0)
860 DepsJsonFileName = ServerApplication;
861 else
862 DepsJsonFileName = ServerApplication.Substring(0, i);
863
864 DepsJsonFileName += ".deps.json";
865
866 Session.Log("deps.json file name: " + DepsJsonFileName);
867
868 if (!File.Exists(DepsJsonFileName))
869 throw new Exception("Invalid server executable. No corresponding deps.json file found.");
870
871 Session.Log("Opening " + DepsJsonFileName);
872
873 string s = File.ReadAllText(DepsJsonFileName);
874
875 Session.Log("Parsing " + DepsJsonFileName);
876
877 if (!(JSON.Parse(s) is Dictionary<string, object> Deps))
878 throw new Exception("Invalid deps.json file. Unable to install.");
879
880 Session.Log("Loading manifest file.");
881
882 XmlDocument Manifest = new XmlDocument()
883 {
884 PreserveWhitespace = true
885 };
886 Manifest.Load(ManifestFile);
887
888 XmlElement Module = Manifest["Module"];
889 string SourceFolder = Path.GetDirectoryName(ManifestFile);
890 string AppFolder = Path.GetDirectoryName(ServerApplication);
891 string DestManifestFileName = Path.Combine(AppFolder, Path.GetFileName(ManifestFile));
892
893 CopyFileIfNewer(Session, ManifestFile, DestManifestFileName, null, false);
894
895 Session.Log("Source folder: " + SourceFolder);
896 Session.Log("App folder: " + AppFolder);
897
898 foreach (XmlNode N in Module.ChildNodes)
899 {
900 if (N is XmlElement E && E.LocalName == "Assembly")
901 {
902 KeyValuePair<string, string> FileNames = GetFileName(E, SourceFolder);
903 string FileName = FileNames.Key;
904 string SourceFileName = FileNames.Value;
905
906 if (CopyFileIfNewer(Session, SourceFileName, Path.Combine(AppFolder, FileName), null, true))
907 {
908 if (FileName.EndsWith(".dll", StringComparison.CurrentCultureIgnoreCase))
909 {
910 string PdbFileName = FileName.Substring(0, FileName.Length - 4) + ".pdb";
911 if (File.Exists(PdbFileName))
912 CopyFileIfNewer(Session, Path.Combine(SourceFolder, PdbFileName), Path.Combine(AppFolder, PdbFileName), null, true);
913 }
914 }
915
916 Assembly A = Assembly.LoadFrom(SourceFileName);
917 AssemblyName AN = A.GetName();
918
919 if (!(Deps is null) && Deps.TryGetValue("targets", out object Obj) && Obj is Dictionary<string, object> Targets)
920 {
921 foreach (KeyValuePair<string, object> P in Targets)
922 {
923 if (P.Value is Dictionary<string, object> Target)
924 {
925 foreach (KeyValuePair<string, object> P2 in Target)
926 {
927 if (P2.Key.StartsWith(ServerName.Name + "/") &&
928 P2.Value is Dictionary<string, object> App &&
929 App.TryGetValue("dependencies", out object Obj2) &&
930 Obj2 is Dictionary<string, object> Dependencies)
931 {
932 Dependencies[AN.Name] = AN.Version.ToString();
933 break;
934 }
935 }
936
937 Dictionary<string, object> Dependencies2 = new Dictionary<string, object>();
938
939 foreach (AssemblyName Dependency in A.GetReferencedAssemblies())
940 Dependencies2[Dependency.Name] = Dependency.Version.ToString();
941
942 Dictionary<string, object> Runtime = new Dictionary<string, object>()
943 {
944 { Path.GetFileName(SourceFileName), new Dictionary<string,object>() }
945 };
946
947 Target[AN.Name + "/" + AN.Version.ToString()] = new Dictionary<string, object>()
948 {
949 { "dependencies", Dependencies2 },
950 { "runtime", Runtime }
951 };
952 }
953 }
954 }
955
956 if (!(Deps is null) && Deps.TryGetValue("libraries", out object Obj3) && Obj3 is Dictionary<string, object> Libraries)
957 {
958 foreach (KeyValuePair<string, object> P in Libraries)
959 {
960 if (P.Key.StartsWith(AN.Name + "/"))
961 {
962 Libraries.Remove(P.Key);
963 break;
964 }
965 }
966
967 Libraries[AN.Name + "/" + AN.Version.ToString()] = new Dictionary<string, object>()
968 {
969 { "type", "project" },
970 { "serviceable", false },
971 { "sha512", string.Empty }
972 };
973 }
974
975 }
976 }
977
978 CopyContent(Session, SourceFolder, AppFolder, ProgramDataFolder, Module);
979
980 Session.Log("Encoding JSON");
981
982 if (!Types.IsInitialized)
983 {
985 typeof(CustomActions).Assembly,
986 typeof(JSON).Assembly);
987 }
988
989 s = JSON.Encode(Deps, true);
990
991 Session.Log("Writing " + DepsJsonFileName);
992 File.WriteAllText(DepsJsonFileName, s, Encoding.UTF8);
993 }
994
995 private static bool CopyFileIfNewer(Session Session, string From, string To, string To2, bool OnlyIfNewer)
996 {
997 if (!File.Exists(From))
998 throw new FileNotFoundException("File not found: " + From);
999
1000 bool Copy1 = From != To;
1001
1002 if (Copy1 && OnlyIfNewer && File.Exists(To))
1003 {
1004 DateTime ToTP = File.GetLastWriteTimeUtc(To);
1005 DateTime FromTP = File.GetLastWriteTimeUtc(From);
1006
1007 if (ToTP >= FromTP)
1008 {
1009 Session.Log("Skipping file. Destination folder contains newer version: " + From);
1010 Copy1 = false;
1011 }
1012 }
1013
1014 if (Copy1)
1015 {
1016 Session.Log("Copying " + From + " to " + To);
1017 File.Copy(From, To, true);
1018 }
1019
1020 if (!string.IsNullOrEmpty(To2))
1021 {
1022 bool Copy2 = From != To2;
1023
1024 if (Copy2 && OnlyIfNewer && File.Exists(To2))
1025 {
1026 DateTime ToTP = File.GetLastWriteTimeUtc(To2);
1027 DateTime FromTP = File.GetLastWriteTimeUtc(From);
1028
1029 if (ToTP >= FromTP)
1030 {
1031 Session.Log("Skipping file. Destination folder contains newer version: " + From);
1032 Copy2 = false;
1033 }
1034 }
1035
1036 if (Copy2)
1037 {
1038 Session.Log("Copying " + From + " to " + To2);
1039 File.Copy(From, To2, true);
1040 }
1041 }
1042
1043 return true;
1044 }
1045
1046 private enum CopyOptions
1047 {
1048 IfNewer,
1049 Always
1050 }
1051
1052 private static void CopyContent(Session Session, string SourceFolder, string AppFolder, string DataFolder, XmlElement Parent)
1053 {
1054 foreach (XmlNode N in Parent.ChildNodes)
1055 {
1056 if (N is XmlElement E)
1057 {
1058 switch (E.LocalName)
1059 {
1060 case "Content":
1061 KeyValuePair<string, string> FileNames;
1062
1063 try
1064 {
1065 FileNames = GetFileName(E, SourceFolder);
1066 }
1067 catch (FileNotFoundException)
1068 {
1069 // Already installed.
1070 break;
1071 }
1072
1073 string FileName = FileNames.Key;
1074 string SourceFileName = FileNames.Value;
1075 CopyOptions CopyOptions = XML.Attribute(E, "copy", CopyOptions.IfNewer);
1076
1077 Session.Log("Content file: " + FileName);
1078
1079 if (!string.IsNullOrEmpty(DataFolder) && !Directory.Exists(DataFolder))
1080 {
1081 Session.Log("Creating folder " + DataFolder + ".");
1082 Directory.CreateDirectory(DataFolder);
1083 }
1084
1085 if (!string.IsNullOrEmpty(AppFolder) && !Directory.Exists(AppFolder))
1086 {
1087 Session.Log("Creating folder " + AppFolder + ".");
1088 Directory.CreateDirectory(AppFolder);
1089 }
1090
1091 try
1092 {
1093 CopyFileIfNewer(Session, SourceFileName,
1094 Path.Combine(DataFolder, FileName),
1095 Path.Combine(AppFolder, FileName),
1096 CopyOptions == CopyOptions.IfNewer);
1097 }
1098 catch (FileNotFoundException)
1099 {
1100 // Already installed by installer.
1101 }
1102 break;
1103
1104 case "Folder":
1105 string Name = XML.Attribute(E, "name");
1106
1107 string SourceFolder2 = Path.Combine(SourceFolder, Name);
1108 string AppFolder2 = Path.Combine(AppFolder, Name);
1109 string DataFolder2 = Path.Combine(DataFolder, Name);
1110
1111 Session.Log("Folder: " + Name,
1112 new KeyValuePair<string, object>("Source", SourceFolder2),
1113 new KeyValuePair<string, object>("App", AppFolder2),
1114 new KeyValuePair<string, object>("Data", DataFolder2));
1115
1116 CopyContent(Session, SourceFolder2, AppFolder2, DataFolder2, E);
1117 break;
1118
1119 case "File":
1120 try
1121 {
1122 FileNames = GetFileName(E, SourceFolder);
1123 }
1124 catch (FileNotFoundException)
1125 {
1126 // Already installed.
1127 break;
1128 }
1129
1130 FileName = FileNames.Key;
1131 SourceFileName = FileNames.Value;
1132
1133 Session.Log("External program file: " + FileName);
1134
1135 if (!string.IsNullOrEmpty(AppFolder) && !Directory.Exists(AppFolder))
1136 {
1137 Session.Log("Creating folder " + AppFolder + ".");
1138 Directory.CreateDirectory(AppFolder);
1139 }
1140
1141 try
1142 {
1143 CopyFileIfNewer(Session, SourceFileName, Path.Combine(AppFolder, FileName), null, false);
1144 }
1145 catch (FileNotFoundException)
1146 {
1147 // Already installed by installer.
1148 }
1149 break;
1150
1151 case "External":
1152 Environment.SpecialFolder SpecialFolder = XML.Attribute(E, "folder", Environment.SpecialFolder.ProgramFiles);
1153 Name = XML.Attribute(E, "name");
1154
1155 SourceFolder2 = Path.Combine(SourceFolder, Name);
1156 AppFolder2 = GetFolderPath(SpecialFolder, Name);
1157 DataFolder2 = Path.Combine(DataFolder, Name);
1158
1159 Session.Log("Folder: " + Name,
1160 new KeyValuePair<string, object>("Source", SourceFolder2),
1161 new KeyValuePair<string, object>("App", AppFolder2),
1162 new KeyValuePair<string, object>("Data", DataFolder2));
1163
1164 CopyContent(Session, SourceFolder2, AppFolder2, DataFolder2, E);
1165 break;
1166 }
1167 }
1168 }
1169 }
1170
1171 private static string GetFolderPath(SpecialFolder SpecialFolder, string Name)
1172 {
1173 string s = Environment.GetFolderPath(SpecialFolder);
1174 string Result = Path.Combine(s, Name);
1175
1176 if (Directory.Exists(Result))
1177 return Result;
1178
1179 if (s.EndsWith("(x86)"))
1180 {
1181 s = s.Substring(0, s.Length - 5).TrimEnd();
1182 string Result2 = Path.Combine(s, Name);
1183
1184 if (Directory.Exists(Result2))
1185 return Result2;
1186 }
1187
1188 return Result;
1189 }
1190
1191 private static void Uninstall(Session Session, string ManifestFile, string ServerApplication, string ProgramDataFolder, bool Remove)
1192 {
1193 // Same code as for custom action UninstallManifest in Waher.IoTGateway.Installers, except:
1194 // * Content files can already be installed in the corresponding application data folder.
1195 // * Assembly files can be removed by uninstaller.
1196
1197 if (string.IsNullOrEmpty(ManifestFile))
1198 throw new Exception("Missing manifest file.");
1199
1200 if (string.IsNullOrEmpty(ServerApplication))
1201 throw new Exception("Missing server application.");
1202
1203 if (string.IsNullOrEmpty(ProgramDataFolder))
1204 {
1205 ProgramDataFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "IoT Gateway");
1206 //if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX) && !Directory.Exists(ProgramDataFolder))
1207 // ProgramDataFolder = ProgramDataFolder.Replace("/usr/share", "/usr/local/share");
1208
1209 Session.Log("Using default program data folder: " + ProgramDataFolder);
1210 }
1211
1212 if (!File.Exists(ServerApplication))
1213 throw new Exception("Server application not found: " + ServerApplication);
1214
1215 Session.Log("Getting assembly name of server.");
1216 AssemblyName ServerName = GetAssemblyName(ServerApplication);
1217 Session.Log("Server assembly name: " + ServerName.ToString());
1218
1219 string DepsJsonFileName;
1220
1221 int i = ServerApplication.LastIndexOf('.');
1222 if (i < 0)
1223 DepsJsonFileName = ServerApplication;
1224 else
1225 DepsJsonFileName = ServerApplication.Substring(0, i);
1226
1227 DepsJsonFileName += ".deps.json";
1228
1229 Session.Log("deps.json file name: " + DepsJsonFileName);
1230
1231 if (!File.Exists(DepsJsonFileName))
1232 throw new Exception("Invalid server executable. No corresponding deps.json file found.");
1233
1234 Session.Log("Opening " + DepsJsonFileName);
1235
1236 string s = File.ReadAllText(DepsJsonFileName);
1237
1238 Session.Log("Parsing " + DepsJsonFileName);
1239
1240 if (!(JSON.Parse(s) is Dictionary<string, object> Deps))
1241 throw new Exception("Invalid deps.json file. Unable to install.");
1242
1243 Session.Log("Loading manifest file.");
1244
1245 XmlDocument Manifest = new XmlDocument();
1246 Manifest.Load(ManifestFile);
1247
1248 XmlElement Module = Manifest["Module"];
1249 string AppFolder = Path.GetDirectoryName(ServerApplication);
1250
1251 Session.Log("App folder: " + AppFolder);
1252
1253 foreach (XmlNode N in Module.ChildNodes)
1254 {
1255 if (N is XmlElement E && E.LocalName == "Assembly")
1256 {
1257 KeyValuePair<string, string> FileNames = GetFileName(E, AppFolder);
1258 string FileName = FileNames.Key;
1259 string AppFileName = FileNames.Value;
1260
1261 Assembly A = Assembly.LoadFrom(AppFileName);
1262 AssemblyName AN = A.GetName();
1263 string Key = AN.Name + "/" + AN.Version.ToString();
1264
1265 if (!(Deps is null) && Deps.TryGetValue("targets", out object Obj) && Obj is Dictionary<string, object> Targets)
1266 {
1267 Targets.Remove(Key);
1268
1269 foreach (KeyValuePair<string, object> P in Targets)
1270 {
1271 if (P.Value is Dictionary<string, object> Target)
1272 {
1273 foreach (KeyValuePair<string, object> P2 in Target)
1274 {
1275 if (P2.Key.StartsWith(ServerName.Name + "/") &&
1276 P2.Value is Dictionary<string, object> App &&
1277 App.TryGetValue("dependencies", out object Obj2) &&
1278 Obj2 is Dictionary<string, object> Dependencies)
1279 {
1280 Dependencies.Remove(AN.Name);
1281 break;
1282 }
1283 }
1284 }
1285 }
1286 }
1287
1288 if (!(Deps is null) && Deps.TryGetValue("libraries", out object Obj3) && Obj3 is Dictionary<string, object> Libraries)
1289 {
1290 foreach (KeyValuePair<string, object> P in Libraries)
1291 {
1292 if (P.Key.StartsWith(AN.Name + "/"))
1293 {
1294 Libraries.Remove(P.Key);
1295 break;
1296 }
1297 }
1298 }
1299
1300 if (Remove)
1301 {
1302 RemoveFile(Session, AppFileName);
1303 if (FileName.EndsWith(".dll", StringComparison.CurrentCultureIgnoreCase))
1304 {
1305 string PdbFileName = FileName.Substring(0, FileName.Length - 4) + ".pdb";
1306 RemoveFile(Session, PdbFileName);
1307 }
1308 }
1309 }
1310 }
1311
1312 Session.Log("Encoding JSON");
1313
1314 if (!Types.IsInitialized)
1315 {
1317 typeof(CustomActions).Assembly,
1318 typeof(JSON).Assembly);
1319 }
1320
1321 s = JSON.Encode(Deps, true);
1322
1323 Session.Log("Writing " + DepsJsonFileName);
1324 File.WriteAllText(DepsJsonFileName, s, Encoding.UTF8);
1325
1326 if (Path.GetDirectoryName(ManifestFile) == AppFolder)
1327 RemoveFile(Session, ManifestFile);
1328 }
1329
1330 private static bool RemoveFile(Session Session, string FileName)
1331 {
1332 if (!File.Exists(FileName))
1333 return false;
1334
1335 Session.Log("Deleting " + FileName);
1336 try
1337 {
1338 File.Delete(FileName);
1339 }
1340 catch (Exception ex)
1341 {
1342 Session.Log("Unable to delete file. Error reported: " + ex.Message);
1343 }
1344
1345 return true;
1346 }
1347
1348 private static KeyValuePair<string, string> GetFileName(XmlElement E, string ReferenceFolder)
1349 {
1350 string FileName = XML.Attribute(E, "fileName");
1351 string AbsFileName = Path.Combine(ReferenceFolder, FileName);
1352 if (File.Exists(AbsFileName))
1353 return new KeyValuePair<string, string>(FileName, AbsFileName);
1354
1355 string AltFolder = XML.Attribute(E, "altFolder");
1356 if (string.IsNullOrEmpty(AltFolder))
1357 throw new FileNotFoundException("File not found: " + AbsFileName);
1358
1359 AbsFileName = Path.Combine(AltFolder, FileName);
1360 if (File.Exists(AbsFileName))
1361 return new KeyValuePair<string, string>(FileName, AbsFileName);
1362
1363 throw new FileNotFoundException("File not found: " + AbsFileName);
1364 }
1365
1366 #endregion
1367
1368 }
1369}
Helps with common JSON-related tasks.
Definition: JSON.cs:14
static object Parse(string Json)
Parses a JSON string.
Definition: JSON.cs:43
static string Encode(string s)
Encodes a string for inclusion in JSON.
Definition: JSON.cs:507
Helps with common XML-related tasks.
Definition: XML.cs:19
static string Attribute(XmlElement E, string Name)
Gets the value of an XML attribute.
Definition: XML.cs:914
Static class that dynamically manages types and interfaces available in the runtime environment.
Definition: Types.cs:14
static bool IsInitialized
If the inventory has been initialized.
Definition: Types.cs:594
static void Initialize(params Assembly[] Assemblies)
Initializes the inventory engine, registering types and interfaces available in Assemblies .
Definition: Types.cs:599