1 /*******************************************************************************
2 
3     Test for ReopenableFilesExt in combination with UnixSocketExt
4 
5     Copyright:
6         Copyright (c) 2017 dunnhumby Germany GmbH.
7         All rights reserved.
8 
9     License:
10         Boost Software License Version 1.0. See LICENSE_BOOST.txt for details.
11         Alternatively, this file may be distributed under the terms of the Tango
12         3-Clause BSD License (see LICENSE_BSD.txt for details).
13 
14 *******************************************************************************/
15 
16 module integrationtest.reopenfiles.main;
17 
18 import ocean.core.Enforce;
19 import ocean.core.Test;
20 import ocean.io.FilePath;
21 import ocean.io.device.File;
22 import ocean.io.select.protocol.task.TaskSelectTransceiver;
23 import ocean.io.select.protocol.generic.ErrnoIOException: IOWarning, IOError;
24 import ocean.meta.types.Qualifiers;
25 import ocean.stdc.posix.sys.un;
26 import ocean.sys.socket.UnixSocket;
27 import ocean.task.Scheduler;
28 import ocean.task.Task;
29 import ocean.util.app.DaemonApp;
30 import ocean.util.test.DirectorySandbox;
31 
32 import core.sys.posix.sys.stat;
33 import core.sys.linux.fcntl;
34 
35 /// Socket class to bind/connect
36 private istring socket_path = "reopensocket.socket";
37 
38 /// Main application class
39 class ReopenableFilesApp : DaemonApp
40 {
41     /// Count of the remaining tasks. Shut downs
42     /// the scheduler when 0.
43     private int task_remaining;
44 
45     /// Task used to send command and confirm the response
46     class SendingTask: Task
47     {
48         /// Command to send
49         cstring command_to_send;
50         /// Response to expect
51         cstring expected_response;
52 
53         /// Constructor
54         this (cstring command_to_send,
55             cstring expected_response)
56         {
57             this.command_to_send = command_to_send;
58             this.expected_response = expected_response;
59         }
60 
61         /// Main task method
62         override void run()
63         {
64             auto socket_address = sockaddr_un.create(socket_path);
65 
66             auto client = new UnixSocket();
67 
68             auto socket_fd = client.socket();
69             enforce(socket_fd >= 0, "socket() call failed!");
70 
71             auto connect_result = client.connect(&socket_address);
72             enforce(connect_result == 0,
73                     "connect() call failed");
74 
75             // set non-blocking mode on the client socket
76             auto existing_flags = fcntl(client.fileHandle(),
77                 F_GETFL, 0);
78             enforce(existing_flags != -1);
79             enforce(fcntl(client.fileHandle(),
80                 F_SETFL, existing_flags | O_NONBLOCK) != -1);
81 
82             // Use the task-blocking tranceiver, so
83             // the scheduler can handle other epoll clients
84             auto transceiver = new TaskSelectTransceiver(client,
85                 new IOWarning(client), new IOError(client));
86             transceiver.write(command_to_send);
87 
88             // Confirm the response!
89             auto buff = new char[this.expected_response.length];
90             transceiver.read(buff);
91             test!("==")(buff, this.expected_response);
92 
93             if (--this.outer.task_remaining == 0)
94             {
95                 theScheduler.shutdown();
96             }
97         }
98     }
99 
100     /// Constructor.
101     this ( )
102     {
103         initScheduler(SchedulerConfiguration.init);
104         theScheduler.exception_handler = (Task, Exception e) {
105             throw e;
106         };
107 
108         istring name = "Application";
109         istring desc = "Testing reopenable files ext";
110 
111         DaemonApp.OptionalSettings settings;
112 
113         super(name, desc, VersionInfo.init, settings);
114     }
115 
116     /// Called after arguments and config file parsing.
117     override protected int run ( Arguments args, ConfigParser config )
118     {
119         this.startEventHandling(theScheduler.epoll);
120 
121         auto original_file = new File;
122         original_file.open("filelocation.txt", File.ReadWriteOpen);
123         this.reopenable_files_ext.register(original_file);
124 
125         // move this file now
126         auto path = new FilePath(original_file.path());
127         path.rename("newfilelocation.txt");
128 
129         auto new_file = new File;
130         new_file.open("filelocation.txt", File.ReadWriteOpen);
131         new_file.write("Pre-reload");
132 
133         // we haven't done reloading
134         test!("==")(original_file.length, 0);
135 
136         auto good_task = new SendingTask("reopen_files filelocation.txt\n",
137             "ACK\n");
138         theScheduler.schedule(good_task);
139 
140         auto bad_task = new SendingTask("reopen_files nonregistered.txt\n",
141             "ERROR: Could not rotate the file 'nonregistered.txt'\n");
142         theScheduler.schedule(bad_task);
143 
144         // Counter used to figure out when to exit the scheduler
145         this.task_remaining = 2;
146 
147         // Spin the task and start working
148         theScheduler.eventLoop();
149 
150         // Test if the file has been reloaded
151         test!("!=")(original_file.length, 0);
152         auto buff = new char["Pre-reload".length];
153         original_file.read(buff);
154         test!("==")(buff, "Pre-reload");
155 
156         return 0;
157     }
158 
159 }
160 
161 version (unittest) {} else
162 void main(istring[] args)
163 {
164     auto sandbox = DirectorySandbox.create(["etc", "log"]);
165     scope (success)
166         sandbox.remove();
167 
168     File.set("etc/config.ini", "[LOG.Root]\n" ~
169                "console = false\n\n" ~
170                "[UNIX_SOCKET]\npath=" ~ socket_path ~ "\nmode=0600");
171 
172     auto app = new ReopenableFilesApp;
173     app.main(args);
174 }