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