File: Porting

package info (click to toggle)
ptop 3.6.2-5
  • links: PTS, VCS
  • area: main
  • in suites: wheezy
  • size: 1,516 kB
  • sloc: ansic: 18,233; sh: 3,493; makefile: 124; awk: 44
file content (229 lines) | stat: -rw-r--r-- 9,615 bytes parent folder | download | duplicates (3)
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
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
Instructions for porting pg_top to other architectures.

This is still a preliminary document.  Suggestions for improvement are
most welcome.

Before you embark on a port, please send me a mail message telling me
what platform you are porting pg_top to.  There are three reasons for
this: (1) I may already have a port, (2) module naming needs to be
centralized, (3) I want to loosely track the various porting efforts.
You do not need to wait for an "okay", but I do want to know that you
are working on it.  And of course, once it is finished, please send me
the module files so that I can add them to the main distribution!

----------

There is one set of functions which extract all the information that
pg_top needs for display.  These functions are collected in to one file.
To make pg_top work on a different architecture simply requires a
different implementation of these functions.  The functions for a
given architecture "foo" are stored in a file called "m_foo.c".  The
Configure script looks for these files and lets the configurer choose
one of them.  This file is called a "module".  The idea is that making
pg_top work on a different machine only requires one additional file and
does not require changes to any existing files.

A module template is included in the distribution, called "m-template".
To write your own module, it is a good idea to start with this template.
If you architecture is similar to one for which a module already
exists, then you can start with that module instead.  If you do so,
remember to change the "AUTHOR" section at the top!

The first comment in a module contains information which is extracted
and used by Configure.  This information is marked with words in all
capitals (such as "SYNOPSIS:" and "LIBS:").  Go look at m-template: it
is fairly self-explanatory.  The text after "LIBS:" (on the same line)
is extracted and included in the LIBS definition of the Makefile so
that extra libraries which may be necessary on some machines (such as
"-lkvm") can be specified in the module.  The text after "CFLAGS:"
(on the same line) is extracted and included as flags in the "CFLAGS"
definition of the Makefile (thus in every compilation step).  This is
used for rare circumstances only:  please don't abuse this hook.

Some operating systems have idiosyncrasies which will affect the form
and/or content of the information pg_top displays.  You may wish to
document such anomalies in the pg_top man page.  This can be done by adding
a file called m_{modulename}.man (where {modulename} is replaced with
the name of the module).  Configure will automatically add this file to
the end of the man page.  See m_sunos4.man for an example.

A module is concerned with two structures:

The statics struct is filled in by machine_init.  Each item is a
pointer to a list of character pointers.  The list is terminated 
with a null pointer.

struct statics
{
    char **procstate_names;	/* process state names */
    char **cpustate_names;	/* cpu state names */
    char **memory_names;	/* memory information names */
};

The system_info struct is filled in by get_system_info and
get_process_info.

struct system_info
{
    int    last_pid;     /* last pid assigned (0 means non-sequential assignment) */
    double load_avg[NUM_AVERAGES];     /* see below */
    int    p_total;      /* total number of processes */
    int    p_active;     /* number of procs considered "active" */
    int    *procstates;  /* array of process state counters */
    int    *cpustates;   /* array of cpustate counters */
    int    *memory;      /* memory information */
};

The last three pointers each point to an array of integers.  The
length of the array is determined by the length of the corresponding
_names array in the statics structure.  Furthermore, if an entry in a
_names array is the empty string ("") then the corresponding value in
the value array will be skipped over.  The display routine displays,
for example, the string procstate_names[0] then the number
procstates[0], then procstate_names[1], procstates[1], etc. until
procstate_names[N] == NULL.  This allows for a tremendous amount of
flexibility in labeling the displayed values.

"procstates" and "memory" are displayed as straight integer values.
Values in "cpustates" are displayed as a percentage * 10.  For
example, the (integer) value 105 is displayed as 10.5%.

These routines must be defined by the machine dependent module.

int machine_init(struct statics *)

	returns 0 on success and -1 on failure,
	prints error messages

char *format_header(char *)

	Returns a string which should be used as the header for the
	process display area.  The argument is a string used to label
	the username column (either "USERNAME" or "UID") and is always
	8 characters in length.

void get_system_info(struct system_info *)

caddr_t get_process_info(struct system_info *, int, int, int (*func)())

	returns a handle to use with format_next_process

char *format_next_process(caddr_t, char *(*func)())

	returns string which describes next process

int proc_compare(caddr_t, caddr_t)

	qsort comparison function

uid_t proc_owner(pid_t)

	Returns the uid owner of the process specified by the pid argument.
	This function is VERY IMPORTANT.  If it fails to do its job, then
	pg_top may pose a security risk.


get_process_info is called immediately after get_system_info.  In
fact, the two functions could be rolled in to one.  The reason they
are not is mostly historical.

Top relies on the existence of a function called "setpriority" to
change a process's priority.  This exists as a kernel call on most 4.3
BSD derived Unixes.  If neither your operating system nor your C
library supplies such a function, then you will need to add one to the
module.  It is defined as follows:

	int setpriority (int dummy, int who, int niceval)

	For the purposes of pg_top, the first argument is meaningless.
	The second is the pid and the third is the new nice value.
	This function should behave just like a kernel call, setting
	errno and returning -1 in case of an error.  This function MUST
	check to make sure that a non-root user does not specify a nice
	value less than the process's current value.  If it detects such
	a condition, it should set errno to EACCES and return -1.
	Other possible ERRNO values:  ESRCH when pid "who" does not exist,
	EPERM when the invoker is not root and not the same as the
	process owner.

Note that pg_top checks process ownership and should never call setpriority
when the invoker's uid is not root and not the same as the process's owner
uid.


The file "machine.h" contains definitions which are useful to modules
and to pg_top.c (such as the structure definitions).  You SHOULD NOT need
to change it when porting to a new platform.

Porting to a new platform should NOT require any changes to existing
files.  You should only need to add m_ files.  If you feel you need a
change in one of the existing files, please contact me so that we can
discuss the details.  I want to keep such changes as general as
possible.

--------

Changes were made to the module interface between 3.5 and 3.6.  Here are
the changes that need to be made to port a 3.5 module to 3.6:

The array that stores memory statistics and is passed back in the system
information structure as "memory" must now be an array of (signed) longs.
This was done to more easily accomodate systems that have gigabytes of
memory.  Since the numbers are supposed to be kilobytes, a long can still
represent up to 2 terabytes.  Look for "int memory_stats[X]" (where "X"
is some arbitrary number) and change it to "long memory_stats[X]".  If
the module support reporting swap information on a separate line, then
its "swap_stats" array also needs to be an array of longs.

The argument to proc_owner should be an int, as in "int pid".  When it is
used in proc_owner it should be cast as necessary.  Many operating systems
will require it to be cast to a pid_t before being compared to the appropriate
element in the proc structure.

In the function format_next_process, the last argument in the main call
to sprintf is the string that contains the command for the process.
Make sure that this last argument is enclosed in a call to "printable".
For example:  "printable(MPP(pp, p_comm))".

The third argument to "get_process_info" needs to be changed to an integer,
typically "int compare_index".  The call to qsort in get_process_info may
be guarded by "if (compare != NULL)".  If it is, remove the if statement.

The other changes to get_process_info depends on whether or not the module
supports multiple sort orders.

To support multiple keys:

Create an array int (*proc_compares[])() and assign to it the list of
comparison functions, NULL terminated.  For example:

int (*proc_compares[])() = {
    compare_cpu,
    compare_size,
    compare_res,
    compare_time,
    NULL };

In get_process_info there is a call to qsort which uses one of the
functions in proc_compares.  It should be changed so that its fourth
argument is "proc_compares[compare_index]".

If the module contains the function "proc_compare", it should be removed.

There should also be a NULL-terminated array of strings which list the names
for the sort keys, for example:

char *ordernames[] = 
{"cpu", "size", "res", "time", NULL};

To indicate that this module supports multiple sort keys, add the following
line in machine_init:

	statics->order_names = ordernames;

If there is no support for multiple keys:

Leave statics->order_names alone and call the comparison function of
your choice in get_process_info, ignoring the third argument.