summaryrefslogtreecommitdiff
path: root/client/bin/fd_stack.py
blob: 201f35da61a73887af0bd76d787b548e7cd894b2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
__author__ = """Copyright Martin J. Bligh, Andy Whitcroft, 2005, 2006"""

import sys, os

class fd_stack:
    """a stack of fd redirects

    Redirects cause existing fd's to be pushed on the stack; restore()
    causes the current set of redirects to be popped, restoring the previous
    filehandle destinations.

    Note that we need to redirect both the sys.stdout type descriptor
    (which print, etc use) and the low level OS numbered descriptor
    which os.system() etc use.
    """

    def __init__(self, fd, filehandle):
        self.fd = fd                            # eg 1
        self.filehandle = filehandle            # eg sys.stdout
        self.stack = [(fd, filehandle)]


    def update_handle(self, new):
        if (self.filehandle == sys.stdout):
            sys.stdout = new
        if (self.filehandle == sys.stderr):
            sys.stderr = new
        self.filehandle = new

    def redirect(self, filename):
        """Redirect output to the specified file

        Overwrites the previous contents, if any.
        """
        self.filehandle.flush()
        fdcopy = os.dup(self.fd)
        self.stack.append( (fdcopy, self.filehandle, 0) )
        # self.filehandle = file(filename, 'w')
        if (os.path.isfile(filename)):
            newfd = os.open(filename, os.O_WRONLY)
        else:
            newfd = os.open(filename, os.O_WRONLY | os.O_CREAT)
        os.dup2(newfd, self.fd)
        os.close(newfd)
        self.update_handle(os.fdopen(self.fd, 'w', 0))


    def tee_redirect(self, filename):
        """Tee output to the specified file

        Overwrites the previous contents, if any.
        """
        self.filehandle.flush()
        #print_to_tty("tee_redirect to " + filename)
        #where_art_thy_filehandles()
        fdcopy = os.dup(self.fd)
        r, w = os.pipe()
        pid = os.fork()
        if pid:                 # parent
            os.close(r)
            os.dup2(w, self.fd)
            os.close(w)
            self.stack.append( (fdcopy, self.filehandle, pid) )
            self.update_handle(os.fdopen(self.fd, 'w', 0))
            #where_art_thy_filehandles()
            #print_to_tty("done tee_redirect to " + filename)
        else:                   # child
            os.close(w)
            os.dup2(r, 0)
            os.dup2(fdcopy, 1)
            os.close(r)
            os.close(fdcopy)
            os.execlp('tee', 'tee', '-a', filename)


    def restore(self):
        """unredirect one level"""
        self.filehandle.flush()
        # print_to_tty("ENTERING RESTORE %d" % self.fd)
        # where_art_thy_filehandles()
        (old_fd, old_filehandle, pid) = self.stack.pop()
        # print_to_tty("old_fd %d" % old_fd)
        # print_to_tty("self.fd %d" % self.fd)
        self.filehandle.close()   # seems to close old_fd as well.
        if pid:
            os.waitpid(pid, 0)
        # where_art_thy_filehandles()
        os.dup2(old_fd, self.fd)
        # print_to_tty("CLOSING FD %d" % old_fd)
        os.close(old_fd)
        # where_art_thy_filehandles()
        self.update_handle(old_filehandle)
        # where_art_thy_filehandles()
        # print_to_tty("EXIT RESTORE %d" % self.fd)


def tee_output_logdir(fn):
    """\
    Method decorator for a class to tee the output to the objects log_dir.
    """
    def tee_logdir_wrapper(self, *args, **dargs):
        self.job.stdout.tee_redirect(os.path.join(self.log_dir, 'stdout'))
        self.job.stderr.tee_redirect(os.path.join(self.log_dir, 'stderr'))
        try:
            result = fn(self, *args, **dargs)
        finally:
            self.job.stderr.restore()
            self.job.stdout.restore()
        return result
    return tee_logdir_wrapper


def __mark(filename, msg):
    file = open(filename, 'a')
    file.write(msg)
    file.close()


def tee_output_logdir_mark(fn):
    def tee_logdir_mark_wrapper(self, *args, **dargs):
        mark = self.__class__.__name__ + "." + fn.__name__
        outfile = os.path.join(self.log_dir, 'stdout')
        errfile = os.path.join(self.log_dir, 'stderr')
        __mark(outfile, "--- START " + mark + " ---\n")
        __mark(errfile, "--- START " + mark + " ---\n")
        self.job.stdout.tee_redirect(outfile)
        self.job.stderr.tee_redirect(errfile)
        try:
            result = fn(self, *args, **dargs)
        finally:
            self.job.stderr.restore()
            self.job.stdout.restore()
            __mark(outfile, "--- END " + mark + " ---\n")
            __mark(errfile, "--- END " + mark + " ---\n")

        return result

    tee_logdir_mark_wrapper.__name__ = fn.__name__
    return tee_logdir_mark_wrapper