1#!/usr/bin/python
2# -*- coding: utf-8 -*-
3"""
4Auxiliary script used to send data between ports on guests.
5
6@copyright: 2010 Red Hat, Inc.
7@author: Jiri Zupka (jzupka@redhat.com)
8@author: Lukas Doktor (ldoktor@redhat.com)
9"""
10import threading
11from threading import Thread
12import os, select, re, random, sys, array, stat
13import fcntl, traceback, signal, time
14
15DEBUGPATH = "/sys/kernel/debug"
16SYSFSPATH = "/sys/class/virtio-ports/"
17DEVPATH = "/dev/virtio-ports/"
18
19exiting = False
20
21class VirtioGuest:
22    """
23    Test tools of virtio_ports.
24    """
25    LOOP_NONE = 0
26    LOOP_POLL = 1
27    LOOP_SELECT = 2
28
29    def __init__(self):
30        self.files = {}
31        self.exit_thread = threading.Event()
32        self.threads = []
33        self.ports = {}
34        self.poll_fds = {}
35        self.catch_signal = None
36        self.use_config = threading.Event()
37
38
39    def _readfile(self, name):
40        """
41        Read file and return content as string
42
43        @param name: Name of file
44        @return: Content of file as string
45        """
46        out = ""
47        try:
48            f = open(name, "r")
49            out = f.read()
50            f.close()
51        except:
52            print "FAIL: Cannot open file %s" % (name)
53
54        return out
55
56
57    def _get_port_status(self, in_files=None):
58        """
59        Get info about ports from kernel debugfs.
60
61        @param in_files: Array of input files.
62        @return: Ports dictionary of port properties
63        """
64        ports = {}
65        not_present_msg = "FAIL: There's no virtio-ports dir in debugfs"
66        if not os.path.ismount(DEBUGPATH):
67            os.system('mount -t debugfs none %s' % (DEBUGPATH))
68        try:
69            if not os.path.isdir('%s/virtio-ports' % (DEBUGPATH)):
70                print not_present_msg
71        except:
72            print not_present_msg
73        else:
74            viop_names = os.listdir('%s/virtio-ports' % (DEBUGPATH))
75            if in_files is not None:
76                dev_names = os.listdir('/dev')
77                rep = re.compile(r"vport[0-9]p[0-9]+")
78                dev_names = filter(lambda x: rep.match(x) is not None, dev_names)
79                if len(dev_names) != len(in_files):
80                    print ("FAIL: Not all ports were successfully initialized "
81                           "in /dev, only %d from %d." % (len(dev_names),
82                                                          len(in_files)))
83                    return
84
85                if len(viop_names) != len(in_files):
86                    print ("FAIL: Not all ports were successfuly initialized "
87                           "in debugfs, only %d from %d." % (len(viop_names),
88                                                             len(in_files)))
89                    return
90
91            for name in viop_names:
92                open_db_file = "%s/virtio-ports/%s" % (DEBUGPATH, name)
93                f = open(open_db_file, 'r')
94                port = {}
95                file = []
96                for line in iter(f):
97                    file.append(line)
98                try:
99                    for line in file:
100                        m = re.match("(\S+): (\S+)", line)
101                        port[m.group(1)] = m.group(2)
102
103                    if port['is_console'] == "yes":
104                        port["path"] = "/dev/hvc%s" % (port["console_vtermno"])
105                        # Console works like a serialport
106                    else:
107                        port["path"] = "/dev/%s" % name
108
109                    if not os.path.exists(port['path']):
110                        print "FAIL: %s not exist" % port['path']
111
112                    sysfspath = SYSFSPATH + name
113                    if not os.path.isdir(sysfspath):
114                        print "FAIL: %s not exist" % (sysfspath)
115
116                    info_name = sysfspath + "/name"
117                    port_name = self._readfile(info_name).strip()
118                    if port_name != port["name"]:
119                        print ("FAIL: Port info does not match "
120                               "\n%s - %s\n%s - %s" %
121                               (info_name , port_name,
122                                "%s/virtio-ports/%s" % (DEBUGPATH, name),
123                                port["name"]))
124                    dev_ppath = DEVPATH + port_name
125                    if not os.path.exists(dev_ppath):
126                        print "FAIL: Symlink %s does not exist." % dev_ppath
127                    if not os.path.realpath(dev_ppath) != "/dev/name":
128                        print "FAIL: Symlink %s is not correct." % dev_ppath
129                except AttributeError:
130                    print ("Bad data on file %s:\n%s. " %
131                           (open_db_file, "".join(file).strip()))
132                    print "FAIL: Bad data on file %s." % open_db_file
133                    return
134
135                ports[port['name']] = port
136                f.close()
137
138        return ports
139
140
141    def check_zero_sym(self):
142        """
143        Check if port /dev/vport0p0 was created.
144        """
145        symlink = "/dev/vport0p0"
146        if os.path.exists(symlink):
147            print "PASS: Symlink %s exists." % symlink
148        else:
149            print "FAIL: Symlink %s does not exist." % symlink
150
151
152    def init(self, in_files):
153        """
154        Init and check port properties.
155        """
156        self.ports = self._get_port_status(in_files)
157
158        if self.ports is None:
159            return
160        for item in in_files:
161            if (item[1] != self.ports[item[0]]["is_console"]):
162                print self.ports
163                print "FAIL: Host console is not like console on guest side\n"
164                return
165
166        print "PASS: Init and check virtioconsole files in system."
167
168
169    class Switch(Thread):
170        """
171        Thread that sends data between ports.
172        """
173        def __init__ (self, in_files, out_files, event,
174                      cachesize=1024, method=0):
175            """
176            @param in_files: Array of input files.
177            @param out_files: Array of output files.
178            @param method: Method of read/write access.
179            @param cachesize: Block to receive and send.
180            """
181            Thread.__init__(self, name="Switch")
182
183            self.in_files = in_files
184            self.out_files = out_files
185            self.exit_thread = event
186            self.method = method
187
188            self.cachesize = cachesize
189
190
191        def _none_mode(self):
192            """
193            Read and write to device in blocking mode
194            """
195            data = ""
196            while not self.exit_thread.isSet():
197                data = ""
198                for desc in self.in_files:
199                    data += os.read(desc, self.cachesize)
200                if data != "":
201                    for desc in self.out_files:
202                        os.write(desc, data)
203
204
205        def _poll_mode(self):
206            """
207            Read and write to device in polling mode.
208            """
209
210            pi = select.poll()
211            po = select.poll()
212
213            for fd in self.in_files:
214                pi.register(fd, select.POLLIN)
215
216            for fd in self.out_files:
217                po.register(fd, select.POLLOUT)
218
219            while not self.exit_thread.isSet():
220                data = ""
221                t_out = self.out_files
222
223                readyf = pi.poll(1.0)
224                for i in readyf:
225                    data += os.read(i[0], self.cachesize)
226
227                if data != "":
228                    while ((len(t_out) != len(readyf)) and not
229                           self.exit_thread.isSet()):
230                        readyf = po.poll(1.0)
231                    for desc in t_out:
232                        os.write(desc, data)
233
234
235        def _select_mode(self):
236            """
237            Read and write to device in selecting mode.
238            """
239            while not self.exit_thread.isSet():
240                ret = select.select(self.in_files, [], [], 1.0)
241                data = ""
242                if ret[0] != []:
243                    for desc in ret[0]:
244                        data += os.read(desc, self.cachesize)
245                if data != "":
246                    ret = select.select([], self.out_files, [], 1.0)
247                    while ((len(self.out_files) != len(ret[1])) and not
248                           self.exit_thread.isSet()):
249                        ret = select.select([], self.out_files, [], 1.0)
250                    for desc in ret[1]:
251                        os.write(desc, data)
252
253
254        def run(self):
255            if (self.method == VirtioGuest.LOOP_POLL):
256                self._poll_mode()
257            elif (self.method == VirtioGuest.LOOP_SELECT):
258                self._select_mode()
259            else:
260                self._none_mode()
261
262
263    class Sender(Thread):
264        """
265        Creates a thread which sends random blocks of data to dst port.
266        """
267        def __init__(self, port, event, length):
268            """
269            @param port: Destination port
270            @param length: Length of the random data block
271            """
272            Thread.__init__(self, name="Sender")
273            self.port = port
274            self.exit_thread = event
275            self.data = array.array('L')
276            for i in range(max(length / self.data.itemsize, 1)):
277                self.data.append(random.randrange(sys.maxint))
278
279        def run(self):
280            while not self.exit_thread.isSet():
281                os.write(self.port, self.data)
282
283
284    def _open(self, in_files):
285        """
286        Open devices and return array of descriptors
287
288        @param in_files: Files array
289        @return: Array of descriptor
290        """
291        f = []
292
293        for item in in_files:
294            name = self.ports[item]["path"]
295            if (name in self.files):
296                f.append(self.files[name])
297            else:
298                try:
299                    self.files[name] = os.open(name, os.O_RDWR)
300                    if (self.ports[item]["is_console"] == "yes"):
301                        print os.system("stty -F %s raw -echo" % (name))
302                        print os.system("stty -F %s -a" % (name))
303                    f.append(self.files[name])
304                except Exception, inst:
305                    print "FAIL: Failed to open file %s" % (name)
306                    raise inst
307        return f
308
309    @staticmethod
310    def pollmask_to_str(mask):
311        """
312        Conver pool mast to string
313
314        @param mask: poll return mask
315        """
316        str = ""
317        if (mask & select.POLLIN):
318            str += "IN "
319        if (mask & select.POLLPRI):
320            str += "PRI IN "
321        if (mask & select.POLLOUT):
322            str += "OUT "
323        if (mask & select.POLLERR):
324            str += "ERR "
325        if (mask & select.POLLHUP):
326            str += "HUP "
327        if (mask & select.POLLMSG):
328            str += "MSG "
329        return str
330
331
332    def poll(self, port, expected, timeout=500):
333        """
334        Pool event from device and print event like text.
335
336        @param file: Device.
337        """
338        in_f = self._open([port])
339
340        p = select.poll()
341        p.register(in_f[0])
342
343        mask = p.poll(timeout)
344
345        maskstr = VirtioGuest.pollmask_to_str(mask[0][1])
346        if (mask[0][1] & expected) == expected:
347            print "PASS: Events: " + maskstr
348        else:
349            emaskstr = VirtioGuest.pollmask_to_str(expected)
350            print "FAIL: Events: " + maskstr + "  Expected: " + emaskstr
351
352
353    def lseek(self, port, pos, how):
354        """
355        Use lseek on the device. The device is unseekable so PASS is returned
356        when lseek command fails and vice versa.
357
358        @param port: Name of the port
359        @param pos: Offset
360        @param how: Relativ offset os.SEEK_{SET,CUR,END}
361        """
362        fd = self._open([port])[0]
363
364        try:
365            os.lseek(fd, pos, how)
366        except Exception, inst:
367            if inst.errno == 29:
368                print "PASS: the lseek failed as expected"
369            else:
370                print inst
371                print "FAIL: unknown error"
372        else:
373            print "FAIL: the lseek unexpectedly passed"
374
375
376    def blocking(self, port, mode=False):
377        """
378        Set port function mode blocking/nonblocking
379
380        @param port: port to set mode
381        @param mode: False to set nonblock mode, True for block mode
382        """
383        fd = self._open([port])[0]
384
385        try:
386            fl = fcntl.fcntl(fd, fcntl.F_GETFL)
387            if not mode:
388                fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK)
389            else:
390                fcntl.fcntl(fd, fcntl.F_SETFL, fl & ~os.O_NONBLOCK)
391
392        except Exception, inst:
393            print "FAIL: Setting (non)blocking mode: " + str(inst)
394            return
395
396        if mode:
397            print "PASS: set to blocking mode"
398        else:
399            print "PASS: set to nonblocking mode"
400
401
402    def __call__(self, sig, frame):
403        """
404        Call function. Used for signal handle.
405        """
406        if (sig == signal.SIGIO):
407            self.sigio_handler(sig, frame)
408
409
410    def sigio_handler(self, sig, frame):
411        """
412        Handler for sigio operation.
413
414        @param sig: signal which call handler.
415        @param frame: frame of caller
416        """
417        if self.poll_fds:
418            p = select.poll()
419            map(p.register, self.poll_fds.keys())
420
421            masks = p.poll(1)
422            print masks
423            for mask in masks:
424                self.poll_fds[mask[0]][1] |= mask[1]
425
426
427    def get_sigio_poll_return(self, port):
428        """
429        Return PASS, FAIL and poll walue in string format.
430
431        @param port: Port to check poll information.
432        """
433        fd = self._open([port])[0]
434
435        maskstr = VirtioGuest.pollmask_to_str(self.poll_fds[fd][1])
436        if (self.poll_fds[fd][0] ^ self.poll_fds[fd][1]):
437            emaskstr = VirtioGuest.pollmask_to_str(self.poll_fds[fd][0])
438            print "FAIL: Events: " + maskstr + "  Expected: " + emaskstr
439        else:
440            print "PASS: Events: " + maskstr
441        self.poll_fds[fd][1] = 0
442
443
444    def set_pool_want_return(self, port, poll_value):
445        """
446        Set value to static variable.
447
448        @param port: Port which should be set excepted mask
449        @param poll_value: Value to check sigio signal.
450        """
451        fd = self._open([port])[0]
452        self.poll_fds[fd] = [poll_value, 0]
453        print "PASS: Events: " + VirtioGuest.pollmask_to_str(poll_value)
454
455
456    def catching_signal(self):
457        """
458        return: True if should set catch signal, False if ignore signal and
459                none when configuration is not changed.
460        """
461        ret = self.catch_signal
462        self.catch_signal = None
463        return ret
464
465
466    def async(self, port, mode=True, exp_val=0):
467        """
468        Set port function mode async/sync.
469
470        @param port: port which should be pooled.
471        @param mode: False to set sync mode, True for sync mode.
472        @param exp_val: Value which should be pooled.
473        """
474        fd = self._open([port])[0]
475
476        try:
477            fcntl.fcntl(fd, fcntl.F_SETOWN, os.getpid())
478            fl = fcntl.fcntl(fd, fcntl.F_GETFL)
479
480            self.use_config.clear()
481            if mode:
482                fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_ASYNC)
483                self.poll_fds[fd] = [exp_val, 0]
484                self.catch_signal = True
485            else:
486                del self.poll_fds[fd]
487                fcntl.fcntl(fd, fcntl.F_SETFL, fl & ~os.O_ASYNC)
488                self.catch_signal = False
489
490            os.kill(os.getpid(), signal.SIGUSR1)
491            self.use_config.wait()
492
493        except Exception, inst:
494            print "FAIL: Setting (a)sync mode: " + str(inst)
495            return
496
497        if mode:
498            print "PASS: Set to async mode"
499        else:
500            print "PASS: Set to sync mode"
501
502
503    def close(self, file):
504        """
505        Close open port.
506
507        @param file: File to close.
508        """
509        descriptor = None
510        path = self.ports[file]["path"]
511        if path is not None:
512            if path in self.files.keys():
513                descriptor = self.files[path]
514                del self.files[path]
515            if descriptor is not None:
516                try:
517                    os.close(descriptor)
518                except Exception, inst:
519                    print "FAIL: Closing the file: " + str(inst)
520                    return
521        print "PASS: Close"
522
523
524    def open(self, in_file):
525        """
526        Direct open devices.
527
528        @param in_file: Array of files.
529        @return: Array of descriptors.
530        """
531        name = self.ports[in_file]["path"]
532        try:
533            self.files[name] = os.open(name, os.O_RDWR)
534            if (self.ports[in_file]["is_console"] == "yes"):
535                print os.system("stty -F %s raw -echo" % (name))
536            print "PASS: Open all filles correctly."
537        except Exception, inst:
538            print "%s\nFAIL: Failed open file %s" % (str(inst), name)
539
540
541    def loopback(self, in_files, out_files, cachesize=1024, mode=LOOP_NONE):
542        """
543        Start a switch thread.
544
545        (There is a problem with multiple opens of a single file).
546
547        @param in_files: Array of input files.
548        @param out_files: Array of output files.
549        @param cachesize: Cachesize.
550        @param mode: Mode of switch.
551        """
552        self.ports = self._get_port_status()
553
554        in_f = self._open(in_files)
555        out_f = self._open(out_files)
556
557        s = self.Switch(in_f, out_f, self.exit_thread, cachesize, mode)
558        s.start()
559        self.threads.append(s)
560        print "PASS: Start switch"
561
562
563    def exit_threads(self):
564        """
565        Function end all running data switch.
566        """
567        self.exit_thread.set()
568        for th in self.threads:
569            print "join"
570            th.join()
571        self.exit_thread.clear()
572
573        del self.threads[:]
574        for desc in self.files.itervalues():
575            os.close(desc)
576        self.files.clear()
577        print "PASS: All threads finished"
578
579
580    def die(self):
581        """
582        Quit consoleswitch.
583        """
584        self.exit_threads()
585        exit()
586
587
588    def send_loop_init(self, port, length):
589        """
590        Prepares the sender thread. Requires clean thread structure.
591        """
592        self.ports = self._get_port_status()
593        in_f = self._open([port])
594
595        self.threads.append(self.Sender(in_f[0], self.exit_thread, length))
596        print "PASS: Sender prepare"
597
598
599    def send_loop(self):
600        """
601        Start sender data transfer. Requires senderprepare run first.
602        """
603        self.threads[0].start()
604        print "PASS: Sender start"
605
606
607    def send(self, port, length=1, mode=True, is_static=False):
608        """
609        Send a data of some length
610
611        @param port: Port to write data
612        @param length: Length of data
613        @param mode: True = loop mode, False = one shoot mode
614        """
615        in_f = self._open([port])
616
617        data = ""
618        writes = 0
619
620        if not is_static:
621            while len(data) < length:
622                data += "%c" % random.randrange(255)
623            try:
624                writes = os.write(in_f[0], data)
625            except Exception, inst:
626                print inst
627        else:
628            while len(data) < 4096:
629                data += "%c" % random.randrange(255)
630        if mode:
631            while (writes < length):
632                try:
633                    writes += os.write(in_f[0], data)
634                except Exception, inst:
635                    print inst
636        if writes >= length:
637            print "PASS: Send data length %d" % writes
638        else:
639            print ("FAIL: Partial send: desired %d, transfered %d" %
640                   (length, writes))
641
642
643    def recv(self, port, length=1, buffer=1024, mode=True):
644        """
645        Recv a data of some length
646
647        @param port: Port to write data
648        @param length: Length of data
649        @param mode: True = loop mode, False = one shoot mode
650        """
651        in_f = self._open([port])
652
653        recvs = ""
654        try:
655            recvs = os.read(in_f[0], buffer)
656        except Exception, inst:
657            print inst
658        if mode:
659            while (len(recvs) < length):
660                try:
661                    recvs += os.read(in_f[0], buffer)
662                except Exception, inst:
663                    print inst
664        if len(recvs) >= length:
665            print "PASS: Recv data length %d" % len(recvs)
666        else:
667            print ("FAIL: Partial recv: desired %d, transfered %d" %
668                   (length, len(recvs)))
669
670
671    def clean_port(self, port, buffer=1024):
672        in_f = self._open([port])
673        ret = select.select([in_f[0]], [], [], 1.0)
674        buf = ""
675        if ret[0]:
676            buf = os.read(in_f[0], buffer)
677        print ("PASS: Rest in socket: ") + str(buf[:10])
678
679
680def is_alive():
681    """
682    Check is only main thread is alive and if guest react.
683    """
684    if threading.activeCount() == 2:
685        print ("PASS: Guest is ok no thread alive")
686    else:
687        threads = ""
688        for thread in threading.enumerate():
689            threads += thread.name + ", "
690        print ("FAIL: On guest run thread. Active thread:" + threads)
691
692
693def compile():
694    """
695    Compile virtio_console_guest.py to speed up.
696    """
697    import py_compile
698    py_compile.compile(sys.path[0] + "/virtio_console_guest.py")
699    print "PASS: compile"
700    sys.exit()
701
702
703def guest_exit():
704    global exiting
705    exiting = True
706
707
708def worker(virt):
709    """
710    Worker thread (infinite) loop of virtio_guest.
711    """
712    global exiting
713    print "PASS: Daemon start."
714    p = select.poll()
715    p.register(sys.stdin.fileno())
716    while not exiting:
717        d = p.poll()
718        if (d[0][1] == select.POLLIN):
719            str = raw_input()
720            try:
721                exec str
722            except:
723                exc_type, exc_value, exc_traceback = sys.exc_info()
724                print "On Guest exception from: \n" + "".join(
725                                traceback.format_exception(exc_type,
726                                                           exc_value,
727                                                           exc_traceback))
728                print "FAIL: Guest command exception."
729        elif (d[0][1] & select.POLLHUP):
730            time.sleep(0.5)
731
732
733def sigusr_handler(sig, frame):
734    pass
735
736
737class Daemon:
738    """
739    Daemonize guest
740    """
741    def __init__(self, stdin, stdout, stderr):
742        """
743        Init daemon.
744
745        @param stdin: path to stdin file.
746        @param stdout: path to stdout file.
747        @param stderr: path to stderr file.
748        """
749        self.stdin = stdin
750        self.stdout = stdout
751        self.stderr = stderr
752
753
754    @staticmethod
755    def is_file_open(path):
756        """
757        Determine process which open file.
758
759        @param path: Path to file.
760        @return [[pid,mode], ... ].
761        """
762        opens = []
763        pids = os.listdir('/proc')
764        for pid in sorted(pids):
765            try:
766                int(pid)
767            except ValueError:
768                continue
769            fd_dir = os.path.join('/proc', pid, 'fd')
770            try:
771                for file in os.listdir(fd_dir):
772                    try:
773                        p = os.path.join(fd_dir, file)
774                        link = os.readlink(os.path.join(fd_dir, file))
775                        if link == path:
776                            mode = os.lstat(p).st_mode
777                            opens.append([pid, mode])
778                    except OSError:
779                        continue
780            except OSError, e:
781                if e.errno == 2:
782                    continue
783                raise
784        return opens
785
786
787    def daemonize(self):
788        """
789        Run guest as a daemon.
790        """
791        try:
792            pid = os.fork()
793            if pid > 0:
794                return False
795        except OSError, e:
796            sys.stderr.write("Daemonize failed: %s\n" % (e))
797            sys.exit(1)
798
799        os.chdir("/")
800        os.setsid()
801        os.umask(0)
802
803        try:
804            pid = os.fork()
805            if pid > 0:
806                sys.exit(0)
807        except OSError, e:
808            sys.stderr.write("Daemonize failed: %s\n" % (e))
809            sys.exit(1)
810
811        sys.stdout.flush()
812        sys.stderr.flush()
813        si = file(self.stdin,'r')
814        so = file(self.stdout,'w')
815        se = file(self.stderr,'w')
816
817        os.dup2(si.fileno(), sys.stdin.fileno())
818        os.dup2(so.fileno(), sys.stdout.fileno())
819        os.dup2(se.fileno(), sys.stderr.fileno())
820
821        sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
822        sys.stderr = os.fdopen(sys.stderr.fileno(), 'w', 0)
823        return True
824
825
826    def start(self):
827        """
828        Start the daemon
829
830        @return: PID of daemon.
831        """
832        # Check for a pidfile to see if the daemon already runs
833        openers = self.is_file_open(self.stdout)
834        rundaemon = False
835        if len(openers) > 0:
836            for i in openers:
837                if i[1] & stat.S_IWUSR:
838                    rundaemon = True
839                    openers.remove(i)
840            if len(openers) > 0:
841                for i in openers:
842                    os.kill(int(i[0]), 9)
843        time.sleep(0.3)
844
845        # Start the daemon
846        if not rundaemon:
847            if self.daemonize():
848                self.run()
849
850
851    def run(self):
852        """
853        Run guest main thread
854        """
855        global exiting
856        virt = VirtioGuest()
857        slave = Thread(target=worker, args=(virt, ))
858        slave.start()
859        signal.signal(signal.SIGUSR1, sigusr_handler)
860        signal.signal(signal.SIGALRM, sigusr_handler)
861        while not exiting:
862            signal.alarm(1)
863            signal.pause()
864            catch = virt.catching_signal()
865            if catch:
866                signal.signal(signal.SIGIO, virt)
867            elif catch is False:
868                signal.signal(signal.SIGIO, signal.SIG_DFL)
869            if catch is not None:
870                virt.use_config.set()
871        print "PASS: guest_exit"
872        sys.exit(0)
873
874
875def main():
876    """
877    Main function with infinite loop to catch signal from system.
878    """
879    if (len(sys.argv) > 1) and (sys.argv[1] == "-c"):
880        compile()
881    stdin = "/tmp/guest_daemon_pi"
882    stdout = "/tmp/guest_daemon_po"
883    stderr = "/tmp/guest_daemon_pe"
884
885    for f in [stdin, stdout, stderr]:
886        try:
887            os.mkfifo(f)
888        except OSError, e:
889            if e.errno == 17:
890                pass
891
892    daemon = Daemon(stdin,
893                    stdout,
894                    stderr)
895    daemon.start()
896
897    d_stdin = os.open(stdin, os.O_WRONLY)
898    d_stdout = os.open(stdout, os.O_RDONLY)
899    d_stderr = os.open(stderr, os.O_RDONLY)
900
901    s_stdin = sys.stdin.fileno()
902    s_stdout = sys.stdout.fileno()
903    s_stderr = sys.stderr.fileno()
904
905    pid = filter(lambda x: x[0] != str(os.getpid()),
906                 daemon.is_file_open(stdout))[0][0]
907
908    print "PASS: Start"
909
910    while 1:
911        ret = select.select([d_stderr,
912                             d_stdout,
913                             s_stdin],
914                            [], [], 1.0)
915        if s_stdin in ret[0]:
916            os.write(d_stdin,os.read(s_stdin, 1))
917        if d_stdout in ret[0]:
918            os.write(s_stdout,os.read(d_stdout, 1024))
919        if d_stderr in ret[0]:
920            os.write(s_stderr,os.read(d_stderr, 1024))
921        if not os.path.exists("/proc/" + pid):
922            sys.exit(0)
923
924    os.close(d_stdin)
925    os.close(d_stdout)
926    os.close(d_stderr)
927
928if __name__ == "__main__":
929    main()
930