1 /*
2 
3     This file is part of the KFloppy program, part of the KDE project
4 
5     Copyright (C) 2002 Adriaan de Groot <groot@kde.org>
6     Copyright (C) 2004, 2005 Nicolas GOUTTE <goutte@kde.org>
7     Copyright (C) 2015, 2016 Wolfgang Bauer <wbauer@tmo.at>
8 
9 
10     This program is free software; you can redistribute it and/or modify
11     it under the terms of the GNU General Public License as published by
12     the Free Software Foundation, version 2.
13 
14     This program is distributed in the hope that it will be useful,
15     but WITHOUT ANY WARRANTY; without even the implied warranty of
16     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17     GNU General Public License for more details.
18 
19     You should have received a copy of the GNU General Public License
20     along with this program; if not, write to the Free Software
21     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
22 
23 */
24 
25 #ifndef FORMAT_H
26 #define FORMAT_H
27 
28 /** \file format.h
29  * This file defines a hierarchy of classes that
30  * can run a sequence of external programs (like
31  * fdformat, mkisofs, etc.) in sequence. Stdout
32  * and stderr of those programs can be captured
33  * and analyzed in order to provide feedback to
34  * the user.
35  *
36  * <ul>
37  * <li>KFAction:           Base class, just for performing some action.
38  * <li>KFActionQueue:     Provides sequencing of KFActions
39  * <li>FloppyAction:      Weird name; handles running a program,
40  *                     understands FD device names. This can be
41  *                     considered the "useful" base class of
42  *                     programming actions.
43  * <li>FDFormat:        Runs fdformat(1) under BSD or Linux.
44  * <li>FATFilesystem:   Creates an msdos (FAT) filesystem.
45  * <li>Ext2Filesystem:  Creates ext2 filesystems.
46  * <li>MinixFilesystem: Creates Minix filesystems, under Linux.
47  * <li>UFSFilesystem:   Creates UFS filesystem, under BSD.
48  * </ul>
49  *
50  * \note Maybe this is overkill, since for floppies all you need is
51  * fdformat(1) and some create-filesystem program like newfs(1)
52  * or mke2fs(1). However, for Zip disks, should they ever be supported,
53  * this is quite useful since you need to dd, fdisk, disklabel, and
54  * newfs them.
55  */
56 
57 #include "debug.h"
58 #include <QObject>
59 #include <QProcess>
60 
61 /**
62  * \brief Abstract base class of actions to be undertaken.
63  *
64  * Basically you can create a KFActionStack (See below)
65  * and push a bunch of actions on it, then run exec()
66  * on the stack and wait for the done() signal.
67  */
68 class KFAction : public QObject
69 {
70     Q_OBJECT
71 
72 public:
73     explicit KFAction(QObject *parent = nullptr);
74     ~KFAction() override;
75 
76 public Q_SLOTS:
77     /**
78      * Exec() should return quickly to ensure that the GUI
79      * thread stays alive. quit() should abort the action.
80      */
81     virtual void exec();
82     /**
83      * Quit aborts the action. No done() signal should
84      * be emitted.
85      */
86     virtual void quit();
87 
88 Q_SIGNALS:
89     /**
90      * done() should always be emitted with this as first
91      * parameter, to avoid sender() magic and the like.
92      * @p success indicates whether the action was
93      * successful.
94      */
95     void done(KFAction *me, bool success);
96 
97     /**
98      * Emit this signal to inform the user of interesting
99      * changes; setting msg to an empty string doesn't
100      * change any visible user message. @p progress
101      * indicates the action's progress (if that can be determined)
102      * and sending -1 leaves the visible indicator unchanged.
103      */
104     void status(const QString &msg, int progress);
105 
106     /** error() displays a box. It interrupts
107      * the user's work and should be used with care.
108      */
109     void error(const QString &msg, const QString &details);
110 };
111 
112 /**
113  * Acts as a queue and executes the actions in the
114  * queue in FIFO order.
115  */
116 class KFActionQueue : public KFAction
117 {
118     Q_OBJECT
119 
120 public:
121     explicit KFActionQueue(QObject *parent = nullptr);
122     ~KFActionQueue() override;
123 
124     /**
125      * Add a KFAction to the queue. When exec() is called,
126      * the actions are called one after the other (if each
127      * action is successful; if any action fails, the whole
128      * queue fails and the unsuccessful action is the last
129      * one run.) Actions become the property of the queue
130      * action. Note that queues can be nested.
131      */
132     void queue(KFAction *);
133 
134     void exec() override;
135 
136 protected Q_SLOTS:
137     void actionDone(KFAction *, bool);
138 
139 private:
140     class KFActionQueue_p *d;
141 };
142 
143 /*
144 ** The remainder of the Actions are concrete ones and
145 ** might better go off to live in another header file,
146 ** but this is only needed if the number of supported
147 ** formats grows enormously.
148 */
149 
150 /**
151  * Description structure for floppy devices.
152  * devices is a list of possible device names (yay
153  * /dev/ consistency) while drive,blocks denotes
154  * fd0 or fd1, and the size of the disk (ought to
155  * be 1440, 1200, 720 or 360. I've never seen a 2880
156  * floppy drive).
157  *
158  * Tracks is pretty bogus; see the internal code for its use.
159  * Similarly, flags are internal too.
160  */
161 
162 using fdinfo = struct {
163     const char *const *devices;
164     int drive;
165     int blocks;
166     int tracks;
167     int flags;
168 };
169 
170 class KProcess;
171 
172 /**
173  * Concrete action for running a single external program.
174  */
175 
176 class FloppyAction : public KFAction
177 {
178     Q_OBJECT
179 
180 public:
181     explicit FloppyAction(QObject *parent = nullptr);
182 
183     /**
184      * Kills the running process, if one exists.
185      */
186     void quit() override;
187 
188     /**
189      * ConfigureDevice() needs to be called prior to exec()
190      * or exec() will fail; this indicates which drive and
191      * density to use.
192      *
193      * \param driveno Number of drive (0 or 1)
194      * \param density Floppy density (in Kilobytes)
195      * \note This same function needs to be
196      * called on all subclasses in order to configure them
197      * for which drive to use, _along_ with their local
198      * configuration functions.
199      */
200 
201     bool configureDevice(int driveno, int density);
202 
203     /**
204      * \brief Configure the device with a device name
205      *
206      * This is an alternate to FloppyAction::configureDevice
207      * for user-given devices.
208      *
209      * \note It does not work for each type of FloppyAction yet
210      */
211     bool configureDevice(const QString &newDeviceName);
212 
213 protected:
214     const fdinfo *deviceInfo; ///< Configuration info (Pointer into list of "/dev/..." entries)
215     QString deviceName; ///< Name of the device
216 
217 protected Q_SLOTS:
218     /**
219      * \brief Provide handling of the exit of the external program
220      */
221     virtual void processDone(int, QProcess::ExitStatus);
222     /**
223      * \brief Provide handling of stdout
224      */
225     virtual void processStdOut(const QString &s);
226     /**
227      * \brief Provide handling stderr.
228      *
229      * The default implementation just sends stderr on
230      * to processStdOut(), so you need reimplement only
231      * FloppyAction::processStdOut if you choose.
232      */
233     virtual void processStdErr(const QString &s);
234 
235 protected:
236     KProcess *theProcess;
237     QString theProcessName; ///< human-readable
238 
239     /**
240      * Sets up connections, calls KProcess::start().
241      * You need to *theProcess << program << args ; first.
242      */
243 
244     bool startProcess();
245 private Q_SLOTS:
246     /**
247      * These functions read stdout/stderr and call
248      * processStdOut()/processStdErr() accordingly
249      */
250     void readStdOut();
251     void readStdErr();
252 };
253 
254 /**
255  * Concrete class that runs fdformat(1)
256  */
257 
258 class FDFormat : public FloppyAction
259 {
260 public:
261     explicit FDFormat(QObject *parent = nullptr);
262 
263     void exec() override;
264 
265     /**
266      * Concrete classes can provide a runtimeCheck
267      * function (heck, this is static, so the name
268      * is up to you) that checks if the required
269      * applications are available. This way, the
270      * calling application can decide not to use
271      * actions whose prerequisites are absent anyway.
272      */
273     static bool runtimeCheck();
274 
275     /** @p verify instructs fdformat(1) to verify the
276      * medium as well.
277      */
278 
279     bool configure(bool verify);
280 
281     void processStdOut(const QString &s) override;
282 
283 protected:
284     static QString fdformatName; ///< path to executable.
285     int formatTrackCount; ///< How many tracks formatted.
286     bool doVerify;
287 };
288 
289 /**
290  * Zero out disk by running dd(1)
291  * \bug As dd terminates with the error "No space left on device", KFloppy aborts
292  */
293 class DDZeroOut : public FloppyAction
294 {
295 public:
296     explicit DDZeroOut(QObject *parent = nullptr);
297 
298     void exec() override;
299 
300     /**
301      * Concrete classes can provide a runtimeCheck
302      * function (heck, this is static, so the name
303      * is up to you) that checks if the required
304      * applications are available. This way, the
305      * calling application can decide not to use
306      * actions whose prerequisites are absent anyway.
307      */
308     static bool runtimeCheck();
309 
310 protected:
311     /**
312      * \brief Provide handling of the exit of the external program
313      */
314     void processDone(int exitCode, QProcess::ExitStatus exitStatus) override;
315 
316 protected:
317     static QString m_ddName; ///< path to executable.
318 };
319 
320 /**
321  * Create an msdos (FAT) filesystem on the floppy.
322  */
323 class FATFilesystem : public FloppyAction
324 {
325 public:
326     explicit FATFilesystem(QObject *parent = nullptr);
327 
328     void exec() override;
329 
330     static bool runtimeCheck();
331 
332     /**
333      * newfs_msdos(1) doesn't support an additional verify,
334      * but Linux mkdosfs(1) does. Enable additional medium
335      * verify with @p verify. Disks can be labeled (@p label) with the
336      * remaining parameters (@p l).
337      */
338     bool configure(bool verify, bool label, const QString &l);
339 
340     /// Parse output
341     void processStdOut(const QString &s) override;
342 
343 protected:
344     static QString newfs_fat;
345 
346     bool doVerify, doLabel;
347     QString label;
348 };
349 
350 /**
351  * Format with Ext2
352  */
353 class Ext2Filesystem : public FloppyAction
354 {
355 public:
356     explicit Ext2Filesystem(QObject *parent = nullptr);
357 
358     void exec() override;
359 
360     static bool runtimeCheck();
361 
362     /// Same args as FATFilesystem::configure
363     bool configure(bool verify, bool label, const QString &l);
364 
365     /// Parse output
366     void processStdOut(const QString &s) override;
367 
368 protected:
369     static QString newfs;
370 
371     bool doVerify, doLabel;
372     QString label;
373 };
374 
375 #ifdef ANY_BSD
376 
377 /**
378  * \brief Format with UFS
379  * \note BSD only
380  */
381 class UFSFilesystem : public FloppyAction
382 {
383 public:
384     explicit UFSFilesystem(QObject *parent = nullptr);
385 
386     void exec() override;
387 
388     static bool runtimeCheck();
389 
390 protected:
391     static QString newfs;
392 
393     bool doVerify, doLabel;
394     QString label;
395 };
396 #endif
397 
398 #ifdef ANY_LINUX
399 /**
400  * \brief Format with Minix
401  * \note Linux only
402  */
403 class MinixFilesystem : public FloppyAction
404 {
405 public:
406     explicit MinixFilesystem(QObject *parent = nullptr);
407 
408     void exec() override;
409 
410     static bool runtimeCheck();
411 
412     /// Same args as FATFilesystem::configure
413     bool configure(bool verify, bool label, const QString &l);
414 
415     /// Parse output
416     void processStdOut(const QString &s) override;
417 
418 protected:
419     static QString newfs;
420 
421     bool doVerify, doLabel;
422     QString label;
423 };
424 #endif
425 
426 /**
427  * Utility function that looks for executables in $PATH
428  * and in /sbin and /usr/sbin.
429  */
430 
431 QString findExecutable(const QString &);
432 
433 #endif
434