예제 #1
0
/*
 *  proc_cache_find_by_pid()
 *	find process info by the process id, if it is not found
 * 	and it is a traceable process then cache it
 */
proc_info_t *proc_cache_find_by_pid(const pid_t pid)
{
	unsigned long h;
	proc_info_t *p;

	pthread_mutex_lock(&proc_cache_mutex);
	h = proc_cache_hash_pid(pid);
	for (p = proc_cache_hash[h]; p; p = p->next) {
		if (p->pid == pid) {
			pthread_mutex_unlock(&proc_cache_mutex);
			return p;
		}
	}
	pthread_mutex_unlock(&proc_cache_mutex);

	/*
	 *  Not found, so add it and return it if it is a legitimate
	 *  process to trace
	 */
	if (!pid_exists(pid) || (pid == getpid()))
		return NULL;

	/*  Be lazy and ignore the parent info lookup */
	return proc_cache_add_at_hash_index(h, pid, 0, false);
}
예제 #2
0
/*
 * Return process pathname executable.
 * Thanks to Robert N. M. Watson:
 * http://fxr.googlebit.com/source/usr.bin/procstat/procstat_bin.c?v=8-CURRENT
 */
static PyObject*
get_process_exe(PyObject* self, PyObject* args)
{
    long pid;
    char pathname[PATH_MAX];
    int error;
    int mib[4];
    size_t size;

    if (! PyArg_ParseTuple(args, "l", &pid)) {
        return NULL;
    }

    mib[0] = CTL_KERN;
    mib[1] = KERN_PROC;
    mib[2] = KERN_PROC_PATHNAME;
    mib[3] = pid;

    size = sizeof(pathname);
    error = sysctl(mib, 4, pathname, &size, NULL, 0);
    if (error == -1) {
        PyErr_SetFromErrno(PyExc_OSError);
        return NULL;
    }
    if (size == 0 || strlen(pathname) == 0) {
        if (pid_exists(pid) == 0) {
            return NoSuchProcess();
        }
        else {
            strcpy(pathname, "");
        }
    }
    return Py_BuildValue("s", pathname);
}
예제 #3
0
/*
 * Return number of threads used by process as a Python integer.
 */
static PyObject*
get_process_num_threads(PyObject* self, PyObject* args)
{
    long pid;
    int err, ret;
    unsigned int info_count = TASK_BASIC_INFO_COUNT;
    mach_port_t task;
    struct task_basic_info tasks_info;
    thread_act_port_array_t thread_list;
    mach_msg_type_number_t thread_count;

    // the argument passed should be a process id
    if (! PyArg_ParseTuple(args, "l", &pid)) {
        return NULL;
    }

    /* task_for_pid() requires special privileges
     * "This function can be called only if the process is owned by the
     * procmod group or if the caller is root."
     * - http://developer.apple.com/documentation/MacOSX/Conceptual/universal_binary/universal_binary_tips/chapter_5_section_19.html
     */
    err = task_for_pid(mach_task_self(), pid, &task);
    if ( err == KERN_SUCCESS) {
        info_count = TASK_BASIC_INFO_COUNT;
        err = task_info(task, TASK_BASIC_INFO, (task_info_t)&tasks_info, &info_count);
        if (err != KERN_SUCCESS) {
                // errcode 4 is "invalid argument" (access denied)
                if (err == 4) {
                    return AccessDenied();
                }

                // otherwise throw a runtime error with appropriate error code
                return PyErr_Format(PyExc_RuntimeError,
                                    "task_info(TASK_BASIC_INFO) failed");
        }
        err = task_threads(task, &thread_list, &thread_count);
        if (err == KERN_SUCCESS) {
            ret = vm_deallocate(task, (vm_address_t)thread_list,
                                thread_count * sizeof(int));
            if (ret != KERN_SUCCESS) {
                printf("vm_deallocate() failed\n");
            }
            return Py_BuildValue("l", (long)thread_count);
        }
        else {
            return PyErr_Format(PyExc_RuntimeError, "task_thread() failed");
        }
    }
    else {
        if (! pid_exists(pid) ) {
            return NoSuchProcess();
        }

        // pid exists, so return AccessDenied error since task_for_pid() failed
        return AccessDenied();
    }
    return NULL;
}
예제 #4
0
/*
 *  proc_cache_add()
 *	explicity add process info to global cache ONLY if it is a traceable process
 */
proc_info_t *proc_cache_add(const pid_t pid, const pid_t ppid, const bool is_thread)
{
	proc_info_t *p;
	unsigned long h;

	if (!pid_exists(pid) || (pid == getpid()))
		return NULL;

	pthread_mutex_lock(&proc_cache_mutex);
	h = proc_cache_hash_pid(pid);
	for (p = proc_cache_hash[h]; p; p = p->next) {
		if (p->pid == pid) {
			pthread_mutex_unlock(&proc_cache_mutex);
			return p;
		}
	}
	pthread_mutex_unlock(&proc_cache_mutex);

	return proc_cache_add_at_hash_index(h, pid, ppid, is_thread);
}
예제 #5
0
/*
 * Return process TCP and UDP connections as a list of tuples.
 * References:
 * - lsof source code: http://goo.gl/SYW79 and http://goo.gl/wNrC0
 * - /usr/include/sys/proc_info.h
 */
static PyObject*
get_process_connections(PyObject* self, PyObject* args)
{
    long pid;
    int pidinfo_result;
    int iterations;
    int i;
    int nb;

    struct proc_fdinfo *fds_pointer;
    struct proc_fdinfo *fdp_pointer;
    struct socket_fdinfo si;

    PyObject *retList = PyList_New(0);
    PyObject *tuple = NULL;
    PyObject *laddr = NULL;
    PyObject *raddr = NULL;
    PyObject *af_filter = NULL;
    PyObject *type_filter = NULL;

    if (! PyArg_ParseTuple(args, "lOO", &pid, &af_filter, &type_filter)) {
        return NULL;
    }

    if (!PySequence_Check(af_filter) || !PySequence_Check(type_filter)) {
        PyErr_SetString(PyExc_TypeError, "arg 2 or 3 is not a sequence");
        return NULL;
    }

    if (pid == 0) {
        return retList;
    }

    pidinfo_result = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, NULL, 0);
    if (pidinfo_result <= 0) {
        goto error;
    }

    fds_pointer = malloc(pidinfo_result);
    pidinfo_result = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, fds_pointer,
                                  pidinfo_result);
    free(fds_pointer);

    if (pidinfo_result <= 0) {
        goto error;
    }

    iterations = (pidinfo_result / PROC_PIDLISTFD_SIZE);

    for (i = 0; i < iterations; i++) {
        errno = 0;
        fdp_pointer = &fds_pointer[i];

        //
        if (fdp_pointer->proc_fdtype == PROX_FDTYPE_SOCKET)
        {
            nb = proc_pidfdinfo(pid, fdp_pointer->proc_fd, PROC_PIDFDSOCKETINFO,
                                &si, sizeof(si));

            // --- errors checking
            if (nb <= 0) {
                if (errno == EBADF) {
                    // let's assume socket has been closed
                    continue;
                }
                if (errno != 0) {
                    return PyErr_SetFromErrno(PyExc_OSError);
                }
                else {
                    return PyErr_Format(PyExc_RuntimeError,
                                "proc_pidinfo(PROC_PIDFDVNODEPATHINFO) failed");
                }
            }
            if (nb < sizeof(si)) {
                return PyErr_Format(PyExc_RuntimeError,
                 "proc_pidinfo(PROC_PIDFDVNODEPATHINFO) failed (buffer mismatch)");
            }
            // --- /errors checking

            //
            int fd, family, type, lport, rport;
            char lip[200], rip[200];
            char *state;
            int inseq;
            PyObject* _family;
            PyObject* _type;

            fd = (int)fdp_pointer->proc_fd;
            family = si.psi.soi_family;
            type = si.psi.soi_kind;

            if (type == 2) {
                type = SOCK_STREAM;
            }

            else if (type == 1) {
                type = SOCK_DGRAM;
            }

            else {
                continue;
            }

            // apply filters
            _family = PyLong_FromLong((long)family);
            inseq = PySequence_Contains(af_filter, _family);
            Py_DECREF(_family);
            if (inseq == 0) {
                continue;
            }

            _type = PyLong_FromLong((long)type);
            inseq = PySequence_Contains(type_filter, _type);
            Py_DECREF(_type);
            if (inseq == 0) {
                continue;
            }

            if (errno != 0) {
                return PyErr_SetFromErrno(PyExc_OSError);
            }

            if (family == AF_INET) {
                inet_ntop(AF_INET,
                          &si.psi.soi_proto.pri_tcp.tcpsi_ini.insi_laddr.ina_46.i46a_addr4,
                          lip,
                          sizeof(lip));
                inet_ntop(AF_INET,
                          &si.psi.soi_proto.pri_tcp.tcpsi_ini.insi_faddr.ina_46.i46a_addr4,
                          rip,
                          sizeof(lip));
            }

            else {
                inet_ntop(AF_INET6,
                          &si.psi.soi_proto.pri_tcp.tcpsi_ini.insi_laddr.ina_6,
                          lip, sizeof(lip));
                inet_ntop(AF_INET6,
                          &si.psi.soi_proto.pri_tcp.tcpsi_ini.insi_faddr.ina_6,
                          lip, sizeof(rip));
            }

            // check for inet_ntop failures
            if (errno != 0) {
                return PyErr_SetFromErrno(PyExc_OSError);
            }

            lport = ntohs(si.psi.soi_proto.pri_tcp.tcpsi_ini.insi_lport);
            rport = ntohs(si.psi.soi_proto.pri_tcp.tcpsi_ini.insi_fport);
            if (type == SOCK_STREAM) {
                state = get_connection_status((int)si.psi.soi_proto.pri_tcp.tcpsi_state);
            }

            else {
                state = "";
            }

            laddr = Py_BuildValue("(si)", lip, lport);
            if (rport != 0) {
                raddr = Py_BuildValue("(si)", rip, rport);
            }

            else {
                raddr = PyTuple_New(0);
            }

            // --- construct python list
            tuple = Py_BuildValue("(iiiNNs)", fd, family, type, laddr, raddr,
                                              state);
            PyList_Append(retList, tuple);
            Py_DECREF(tuple);
            // --- /construct python list
        }
    }

    return retList;

error:
    if (errno != 0) {
        return PyErr_SetFromErrno(PyExc_OSError);
    }

    else if (! pid_exists(pid) ) {
        return NoSuchProcess();
    }

    else {
        return PyErr_Format(PyExc_RuntimeError,
                            "proc_pidinfo(PROC_PIDLISTFDS) failed");
    }
}
예제 #6
0
/*
 * Return process open files as a Python tuple.
 * References:
 * - lsof source code: http://goo.gl/SYW79 and http://goo.gl/m78fd
 * - /usr/include/sys/proc_info.h
 */
static PyObject*
get_process_open_files(PyObject* self, PyObject* args)
{
    long pid;
    int pidinfo_result;
    int iterations;
    int i;
    int nb;

    struct proc_fdinfo *fds_pointer;
    struct proc_fdinfo *fdp_pointer;
    struct vnode_fdinfowithpath vi;

    PyObject *retList = PyList_New(0);
    PyObject *tuple = NULL;

    if (! PyArg_ParseTuple(args, "l", &pid)) {
        return NULL;
    }

    pidinfo_result = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, NULL, 0);
    if (pidinfo_result <= 0) {
        goto error;
    }

    fds_pointer = malloc(pidinfo_result);
    pidinfo_result = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, fds_pointer,
                                  pidinfo_result);
    free(fds_pointer);

    if (pidinfo_result <= 0) {
        goto error;
    }

    iterations = (pidinfo_result / PROC_PIDLISTFD_SIZE);

    for (i = 0; i < iterations; i++) {
        fdp_pointer = &fds_pointer[i];

        //
        if (fdp_pointer->proc_fdtype == PROX_FDTYPE_VNODE)
        {
            nb = proc_pidfdinfo(pid,
                                fdp_pointer->proc_fd,
                                PROC_PIDFDVNODEPATHINFO,
                                &vi,
                                sizeof(vi));

            // --- errors checking
            if (nb <= 0) {
                if ((errno == ENOENT) || (errno == EBADF)) {
                    // no such file or directory or bad file descriptor;
                    // let's assume the file has been closed or removed
                    continue;
                }
                if (errno != 0) {
                    return PyErr_SetFromErrno(PyExc_OSError);
                }
                else
                    return PyErr_Format(PyExc_RuntimeError,
                                "proc_pidinfo(PROC_PIDFDVNODEPATHINFO) failed");
            }
            if (nb < sizeof(vi)) {
                return PyErr_Format(PyExc_RuntimeError,
                 "proc_pidinfo(PROC_PIDFDVNODEPATHINFO) failed (buffer mismatch)");
            }
            // --- /errors checking

            // --- construct python list
            tuple = Py_BuildValue("(si)", vi.pvip.vip_path,
                                          (int)fdp_pointer->proc_fd);
            PyList_Append(retList, tuple);
            Py_DECREF(tuple);
            // --- /construct python list
        }
    }

    return retList;

error:
    if (errno != 0) {
        return PyErr_SetFromErrno(PyExc_OSError);
    }

    else if (! pid_exists(pid) ) {
        return NoSuchProcess();
    }

    else {
        return PyErr_Format(PyExc_RuntimeError,
                            "proc_pidinfo(PROC_PIDLISTFDS) failed");
    }
}
예제 #7
0
/*
 * Return process threads
 */
static PyObject*
get_process_threads(PyObject* self, PyObject* args)
{
    long pid;
    int err, j, ret;
    kern_return_t kr;
    unsigned int info_count = TASK_BASIC_INFO_COUNT;
    mach_port_t task;
    struct task_basic_info tasks_info;
    thread_act_port_array_t thread_list;
    thread_info_data_t thinfo;
    thread_basic_info_t basic_info_th;
    mach_msg_type_number_t thread_count, thread_info_count;

    PyObject* retList = PyList_New(0);
    PyObject* pyTuple = NULL;

    // the argument passed should be a process id
    if (! PyArg_ParseTuple(args, "l", &pid)) {
        return NULL;
    }

    // task_for_pid() requires special privileges
    err = task_for_pid(mach_task_self(), pid, &task);
    if (err != KERN_SUCCESS) {
        if (! pid_exists(pid) ) {
            return NoSuchProcess();
        }
        return AccessDenied();
    }

    info_count = TASK_BASIC_INFO_COUNT;
    err = task_info(task, TASK_BASIC_INFO, (task_info_t)&tasks_info, &info_count);
    if (err != KERN_SUCCESS) {
        // errcode 4 is "invalid argument" (access denied)
        if (err == 4) {
            return AccessDenied();
        }
        // otherwise throw a runtime error with appropriate error code
        return PyErr_Format(PyExc_RuntimeError,
                            "task_info(TASK_BASIC_INFO) failed");
    }

    err = task_threads(task, &thread_list, &thread_count);
    if (err != KERN_SUCCESS) {
        return PyErr_Format(PyExc_RuntimeError, "task_threads() failed");
    }

    for (j = 0; j < thread_count; j++) {
        thread_info_count = THREAD_INFO_MAX;
        kr = thread_info(thread_list[j], THREAD_BASIC_INFO,
                         (thread_info_t)thinfo, &thread_info_count);
        if (kr != KERN_SUCCESS) {
            return PyErr_Format(PyExc_RuntimeError, "thread_info() failed");
        }
        basic_info_th = (thread_basic_info_t)thinfo;
        // XXX - thread_info structure does not provide any process id;
        // the best we can do is assigning an incremental bogus value
        pyTuple = Py_BuildValue("Iff", j + 1,
                    (float)basic_info_th->user_time.microseconds / 1000000.0,
                    (float)basic_info_th->system_time.microseconds / 1000000.0
                  );
        PyList_Append(retList, pyTuple);
        Py_XDECREF(pyTuple);
    }

    ret = vm_deallocate(task, (vm_address_t)thread_list,
                        thread_count * sizeof(int));
    if (ret != KERN_SUCCESS) {
        printf("vm_deallocate() failed\n");
    }

    return retList;
}
예제 #8
0
/*
 * Return a tuple of RSS and VMS memory usage.
 */
static PyObject*
get_memory_info(PyObject* self, PyObject* args)
{
    long pid;
    int err;
    unsigned int info_count = TASK_BASIC_INFO_COUNT;
    mach_port_t task;
    struct task_basic_info tasks_info;
    vm_region_basic_info_data_64_t  b_info;
    vm_address_t address = GLOBAL_SHARED_TEXT_SEGMENT;
    vm_size_t size;
    mach_port_t object_name;

    // the argument passed should be a process id
    if (! PyArg_ParseTuple(args, "l", &pid)) {
        return NULL;
    }

    /* task_for_pid() requires special privileges
     * "This function can be called only if the process is owned by the
     * procmod group or if the caller is root."
     * - http://developer.apple.com/documentation/MacOSX/Conceptual/universal_binary/universal_binary_tips/chapter_5_section_19.html */
    err = task_for_pid(mach_task_self(), pid, &task);
    if ( err == KERN_SUCCESS) {
        info_count = TASK_BASIC_INFO_COUNT;
        err = task_info(task, TASK_BASIC_INFO, (task_info_t)&tasks_info, &info_count);
        if (err != KERN_SUCCESS) {
                if (err == 4) {
                    // errcode 4 is "invalid argument" (access denied)
                    return AccessDenied();
                }
                // otherwise throw a runtime error with appropriate error code
                return PyErr_Format(PyExc_RuntimeError,
                                    "task_info(TASK_BASIC_INFO) failed");
        }

        /* Issue #73 http://code.google.com/p/psutil/issues/detail?id=73
         * adjust the virtual memory size down to account for
         * shared memory that task_info.virtual_size includes w/every process
         */
        info_count = VM_REGION_BASIC_INFO_COUNT_64;
        err = vm_region_64(task, &address, &size, VM_REGION_BASIC_INFO,
            (vm_region_info_t)&b_info, &info_count, &object_name);
        if (err == KERN_SUCCESS) {
            if (b_info.reserved && size == (SHARED_TEXT_REGION_SIZE) &&
                tasks_info.virtual_size > (SHARED_TEXT_REGION_SIZE + SHARED_DATA_REGION_SIZE))
            {
                tasks_info.virtual_size -= (SHARED_TEXT_REGION_SIZE + SHARED_DATA_REGION_SIZE);
            }
        }
    }

    else {
        if (! pid_exists(pid) ) {
            return NoSuchProcess();
        }

        // pid exists, so return AccessDenied error since task_for_pid() failed
        return AccessDenied();
    }

    return Py_BuildValue("(ll)", tasks_info.resident_size, tasks_info.virtual_size);
}
예제 #9
0
/*
 * Return a Python tuple (user_time, kernel_time)
 */
static PyObject*
get_cpu_times(PyObject* self, PyObject* args)
{
    long pid;
    int err;
    unsigned int info_count = TASK_BASIC_INFO_COUNT;
    task_port_t task;  // = (task_port_t)NULL;
    time_value_t user_time, system_time;
    struct task_basic_info tasks_info;
    struct task_thread_times_info task_times;

    if (! PyArg_ParseTuple(args, "l", &pid)) {
        return NULL;
    }

    /*  task_for_pid() requires special privileges
     * "This function can be called only if the process is owned by the
     * procmod group or if the caller is root."
     * - http://developer.apple.com/documentation/MacOSX/Conceptual/universal_binary/universal_binary_tips/chapter_5_section_19.html  */
    err = task_for_pid(mach_task_self(), pid, &task);
    if ( err == KERN_SUCCESS) {
        info_count = TASK_BASIC_INFO_COUNT;
        err = task_info(task, TASK_BASIC_INFO, (task_info_t)&tasks_info, &info_count);
        if (err != KERN_SUCCESS) {
                // errcode 4 is "invalid argument" (access denied)
                if (err == 4) {
                    return AccessDenied();
                }

                // otherwise throw a runtime error with appropriate error code
                return PyErr_Format(PyExc_RuntimeError,
                                   "task_info(TASK_BASIC_INFO) failed");
        }

        info_count = TASK_THREAD_TIMES_INFO_COUNT;
        err = task_info(task, TASK_THREAD_TIMES_INFO,
                        (task_info_t)&task_times, &info_count);
        if (err != KERN_SUCCESS) {
                // errcode 4 is "invalid argument" (access denied)
                if (err == 4) {
                    return AccessDenied();
                }
                return PyErr_Format(PyExc_RuntimeError,
                                   "task_info(TASK_BASIC_INFO) failed");
        }
    }

    else { // task_for_pid failed
        if (! pid_exists(pid) ) {
            return NoSuchProcess();
        }
        // pid exists, so return AccessDenied error since task_for_pid() failed
        return AccessDenied();
    }

    float user_t = -1.0;
    float sys_t = -1.0;
    user_time = tasks_info.user_time;
    system_time = tasks_info.system_time;

    time_value_add(&user_time, &task_times.user_time);
    time_value_add(&system_time, &task_times.system_time);

    user_t = (float)user_time.seconds + ((float)user_time.microseconds / 1000000.0);
    sys_t = (float)system_time.seconds + ((float)system_time.microseconds / 1000000.0);
    return Py_BuildValue("(dd)", user_t, sys_t);
}
예제 #10
0
void OS_get_table()
{
    /* dir walker storage */
    DIR             *dir;
    struct dirent   *dir_ent, *dir_result;
    
    /* all our storage is going to be here */
    struct obstack  mem_pool;
    
    /* container for scaped process values */
    struct procstat *prs;

    /* string containing our local copy of format_str, elements will be
     * lower cased if we are able to figure them out */
    char            *format_str;

    /* initlize a small memory pool for this function */
    obstack_init(&mem_pool);

    /* put the dirent on the obstack, since it's rather large */
    dir_ent = obstack_alloc(&mem_pool, sizeof(struct dirent));

    if ((dir = opendir("/proc")) == NULL)
        return;

    /* Iterate through all the process entries (numeric) under /proc */
    while(readdir_r(dir, dir_ent, &dir_result) == 0 && dir_result) {

        /* Only look at this file if it's a proc id; that is, all numbers */
        if(!is_pid(dir_result->d_name))
            continue;

        /* allocate container for storing process values */
        prs = obstack_alloc(&mem_pool, sizeof(struct procstat));
        bzero(prs, sizeof(struct procstat));

        /* intilize the format string */
        obstack_printf(&mem_pool, get_string(STR_DEFAULT_FORMAT));
        obstack_1grow(&mem_pool, '\0');
        format_str = (char *) obstack_finish(&mem_pool);

        /* get process' uid/guid */
        get_user_info(dir_result->d_name, format_str, prs, &mem_pool);

        /* scrape /proc/${pid}/stat */
        if (get_proc_stat(dir_result->d_name, format_str, prs, &mem_pool) == false) {
            /* did the pid directory go away mid flight? */
            if (pid_exists(dir_result->d_name, &mem_pool) == false)
                continue;
        }

        /* correct values (times) found in /proc/${pid}/stat */
        fixup_stat_values(format_str, prs);

		/* get process' cmndline */
		get_proc_cmndline(dir_result->d_name, format_str, prs, &mem_pool);

        /* get process' cwd & exec values from the symblink */
        eval_link(dir_result->d_name, "cwd", F_CWD, &prs->cwd, format_str,
            &mem_pool);
        eval_link(dir_result->d_name, "exe", F_EXEC, &prs->exec, format_str,
            &mem_pool);

        /* scapre from /proc/{$pid}/status */
        get_proc_status(dir_result->d_name, format_str, prs, &mem_pool);

		/* calculate precent cpu & mem values */
		calc_prec(format_str, prs, &mem_pool);

        /* Go ahead and bless into a perl object */
        bless_into_proc(format_str, field_names,
            prs->uid,			
            prs->gid,			
            prs->pid,			
            prs->comm,			
            prs->ppid,			
            prs->pgrp,			
            prs->sid,			
            prs->tty,			
            prs->flags,			
            prs->minflt,		
            prs->cminflt,		
            prs->majflt,		
            prs->cmajflt,		
            prs->utime,			
            prs->stime,			
            prs->cutime,		
            prs->cstime,		
            prs->priority,		
            prs->start_time,	
            prs->vsize,			
            prs->rss,			
            prs->wchan,			
	    prs->time,			
	    prs->ctime,			
            prs->state,			
            prs->euid,
            prs->suid,
            prs->fuid,
            prs->egid,
            prs->sgid,
            prs->fgid,
            prs->pctcpu,
            prs->pctmem,
            prs->cmndline,
            prs->exec,
            prs->cwd
        );

        /* we want a new prs, for the next itteration */
        obstack_free(&mem_pool, prs);
    }
    
    closedir(dir);

    /* free all our tempoary memory */
    obstack_free(&mem_pool, NULL);
}